← DeepLoc

Docs · Updated September 2026

DeepLoc user guide

Everything you need to set up DeepLoc and make it your own.

Get DeepLoc on the Asset Store

DeepLoc is a Unity localization tool. It scans UI text and assets in your project, translates them with DeepL, and applies the correct language at runtime.

1. Introduction

What DeepLoc does

  1. Scan — finds text in scenes, prefabs, and scripts, plus localizable assets (images, audio, video, etc.)
  2. Translate — sends text to DeepL and stores translations in a central data asset
  3. Apply — at runtime, components read from that asset and update when the player changes language

Module structure

Module Folder Purpose
Runtime Core/ Build-safe scripts used in your game
Editor Editor/ Scanning, translation, and setup tools
Sample Sample/ Demo scene and scripts (optional — safe to delete)

Requirements

  • Unity 2021.3 LTS or newer
  • TextMeshPro package
  • DeepL API key — required for translation in the editor; not required to run the sample scene in Play mode

Third-party software

Asset uses Google Noto fonts (Noto Naskh Arabic, Noto Sans Hebrew, Noto Sans Devanagari, Noto Sans JP, Noto Sans KR, Noto Sans SC) under the SIL Open Font License, Version 1.1; see Third-Party Notices.txt in the package root for full license text.

2. Quick Start

Path A — New project setup

On first import the DeepLoc Editor opens on Home with a setup guide (reopen it any time with Tools → DeepLoc → Setup Wizard or ? → Setup guide):

  1. Localization data: click Create Localization Data (creates Assets/DeepLoc/Data/LocalizationData.asset)
  2. DeepL API key (optional): paste your key and Test API Key, or skip and add it later in Settings
  3. Target languages: add the languages to translate into (e.g. DE, FR, HI)
  4. Fonts (optional): Fix fonts so TextMesh Pro can render every language
  5. First scan (optional): pick what to scan and click Scan & Translate

Click Finish setup and Home switches to the project dashboard.

Path B — Run the sample scene now

  1. Open Sample/Scenes/DeepLocShowcase.unity
  2. Press Play
  3. Pick a language in the top bar (or on the Welcome page) and browse the pages on the left

The sample has its own data asset (Sample/Data/SampleLocalizationData.asset) with translations for 7 languages, so no import step or DeepL key is needed. See Sample Scene.

Menu Opens
Tools → DeepLoc → Editor Main localization window
Tools → DeepLoc → Setup Wizard Opens the setup guide on the Home page
Tools → DeepLoc → Script Scanner Opens the Scripts page of the DeepLoc Editor
GameObject → DeepLoc → Localization → Language Selector Creates a language dropdown under Canvas

3. Core Concepts

LocalizationData

A ScriptableObject that stores everything:

  • Text entries — key, original text (EN), translations per language
  • Asset entries — key, asset type, per-language assets (sprites, clips, etc.)

Create one with Create Localization Data on the Home page, or Create new… in the toolbar’s data dropdown.

LocalizationManager

A singleton in your scene (use the prefab at Prefabs/LocalizationManager.prefab). It:

  • Holds the current language
  • Refreshes all localized components when language changes
  • Provides GetTranslation() and GetAsset<T>() for scripts

Keys

Each piece of localized content has a string key (e.g. Change_Language). Optionally click Generate LocKeys (Home → Quick actions) to write Core/Generated/LocKeys.cs with a constant for every key.

Default language

English (EN) is the source language. If a translation is missing at runtime, GetTranslation returns N/A_Key_LANG (e.g. N/A_boy_DE) so you can spot gaps easily.

4. Runtime Components

Add these to GameObjects in your scene. Each reads a key from LocalizationData and updates when SetLanguage is called.

Component Unity target Localizes
LocalizedText Text, TMP UI, TMP 3D String
LocalizedImage UI Image Sprite
LocalizedSpriteRenderer SpriteRenderer Sprite
LocalizedRawImage RawImage Texture2D
LocalizedMaterial Renderer Material
LocalizedVideo VideoPlayer VideoClip
LocalizedPrefab Transform (child) GameObject prefab
LocalizedAudioSource AudioSource AudioClip
LanguageSelector Dropdown Populates languages, calls SetLanguage
TextBackgroundScaler RectTransform + text child Resizes background to fit text
IgnoreLocalization Any Excludes object from scene scan

Quick add: Right-click a Text, TMP, Image, or other supported component in the Inspector → Localize this component. This adds the localized component and creates the entry in LocalizationData.

5. Runtime API

using Cubehole.Localization;

// Change language (updates all localized components)
LocalizationManager.Instance.SetLanguage("DE");

// Get translated text
string text = LocalizationManager.GetTranslation("MyKey");

// Text with variables — translation uses {0}, {1}, etc. (any argument types)
string greeting = LocalizationManager.GetTranslationFormat("GreetingKey", playerName, scoreValue);

// Pluralization — picks the right form by count
string items = LocalizationManager.GetTranslation("ItemsKey", itemCount);

// Get localized asset
Sprite sprite = LocalizationManager.GetAsset<Sprite>("MyImageKey");

Also available:

  • Variables — use GetTranslationFormat for format arguments. GetTranslation(key, "text") treats a string second argument as a language code.
  • RTL — DeepLoc shapes Arabic/Persian (contextual glyphs, lam-alef, tashkeel) and reorders Hebrew for display. LocalizedText applies the correct transform for TextMeshPro (isRightToLeftText) or legacy Unity UI Text. Public GetTranslation returns TMP-ready RTL text; use GetTranslationLogical when you need the unshaped string. Run Tools → DeepLoc → Expand RTL Font Atlases once so Arabic presentation forms and the Hebrew font are in TMP fallbacks. Layout/alignment mirroring is still your project’s responsibility.
  • Devanagari (Hindi, Marathi, Nepali) — TextMesh Pro cannot join Devanagari letters, so हिन्दी would show the vowel sign ि after ह and conjuncts with a visible virama. Click Bake conjuncts on the Fonts page (or Tools → DeepLoc → Bake Devanagari Conjuncts) once: it bakes the font’s conjunct, half-form, rakar/reph and ि-length glyphs into NotoSansDevanagari-Medium Conjuncts SDF (added as a fallback of the Devanagari font) and writes Resources/DeepLoc/DevanagariShapingData. LocalizedText, LanguageSelector and GetTranslation then shape any text containing Devanagari, whatever the current language. Legacy UI Text, fonts that cannot reach the baked asset, and projects without a bake get reorder-only shaping (ि placed correctly, conjuncts with a visible virama). For your own components use DisplayTextShaper.Shape(GetTranslationLogical(key), language, RtlOutputKind.Tmp, text.font). Bake again after changing the Devanagari font (it needs a font with glyph names, like the bundled Noto). Marathi-specific letter forms (eyelash ra, .MAR alternates) are not selected.
  • Pseudo-localization — enable on LocalizationManager to stress-test UI with expanded strings (rich-text tags and {0}/%s placeholders are left intact)
  • Custom localized components — implement ILocalizedAsset, call LocalizationManager.Register(this) in OnEnable / Unregister(this) in OnDisable, and apply the current language in OnEnable. In Play mode only registered components receive language changes. Alternatively subscribe to LocalizationManager.OnLanguageChanged.
  • Language persistence — LocalizationManager reads PlayerPrefs("Language") on startup if set (including EN); LanguageSelector saves it. Auto-detect of the system language only applies when nothing is saved.

6. Editor Tools

DeepLoc Editor (Tools → DeepLoc → Editor)

The window has a toolbar across the top, a page list on the left, and a status bar along the bottom.

Toolbar

Control What it does
Data dropdown Switch between LocalizationData assets, create a new one, or select it in the Project window
Preview Language shown by localized components in the open scene (needs a LocalizationManager)
Pseudo Pseudo-localization on/off, to spot truncation and hard-coded text. Prefix, suffix, and expansion are under Settings → Pseudo-localization
DeepL pill Key status and characters left this billing period. Click to test the key, refresh usage, or open DeepL settings
? Setup guide and this user guide

Pages

Page What it does
Home Until setup is finished: the setup guide (see Quick Start). After that: Setup checklist with one-click fixes (data asset, LocalizationManager, DeepL key, target languages, duplicate keys, font glyph coverage), coverage bar per language, and quick actions (Refresh scene text, Generate LocKeys)
Strings The place to add, edit, and delete text keys. The table shows every key × language with % translated per language in the column headers; missing cells are highlighted, locked ones show a lock, and keys with problems (duplicate or empty key, empty source, a translation missing a placeholder like {0}) are marked in red. Add keys in the row under the table (the key is suggested from the source text). Double-click a cell or press F2 to edit it in place; Delete removes the selected keys (undoable). The panel on the right edits the selected key: rename (optionally updating LocalizedText components in open scenes), category, source text, context, each translation with lock and DeepL, plural forms, platform overrides, and where it is used. Select several keys to set a category, lock/unlock, or delete them together. Filters: search, category, Missing, Problems, Unused (one-off project scan)
Assets The place to add, edit, and delete localized asset keys (sprites, textures, materials, video, audio, prefabs). Drag assets from the Project window onto the table to create one key per asset, or use the row under the table (key, type, optional source asset). Languages without an asset are marked fallback (they use the source asset); click an asset to find it in the Project window. The panel on the right edits the selected key: rename (optionally updating localized asset components in open scenes), type, category, context, the asset for each language with a lock, and where it is used. Select several keys to set a category, lock/unlock, or delete them. Filters: search, type, category, Missing, Problems (duplicate keys, assets whose type does not match the key)
Scan Choose what to scan (UI Text, TMP, C# scripts, asset component types) and where (active scene, build scenes, prefabs), then Scan & Translate. Also lists text in the active scene without LocalizedText, summarizes hard-coded script strings with a link to the Scripts page (Section 7), and removes localized components from a selection
Scripts Finds string literals in your C# scripts and replaces them with GetTranslation calls; see Section 7
Fonts Fix Fonts adds package fonts to TMP fallbacks; Expand RTL Atlases adds Arabic presentation forms + Hebrew TMP; Bake conjuncts bakes Devanagari conjunct glyphs (see Devanagari in Section 5); Validate Glyphs flags unsupported characters
Sheets Push/pull sync with translators; see Section 8
Settings DeepL API key, target languages, pseudo-localization prefix/suffix/expansion, variable protection (regex masks for {0}, %s, rich text tags), Addressables (Section 9), and logging

Status bar shows the latest message (warnings and errors appear under the toolbar until dismissed). On the Strings page it holds Translate missing, which shows the approximate number of characters DeepL will receive, and Translate selected. When translations are missing, Home also shows Translate missing under Quick actions. While a scan or translation runs, the status bar shows its progress with Pause / Resume and Stop (finished translations are kept). Log opens the scan / translate log above the status bar.

The LocalizationData asset’s Inspector only shows a summary; double-click the asset (or click Open in DeepLoc Editor) to edit it.

Scan & Translate workflow

  1. On the Scan page, choose scope: Active Scene, All Scenes in Build, or Selected Prefabs
  2. Toggle what to scan: UI text, scripts, images, audio, etc.
  3. Click Scan & Translate (or Translate missing on Home / the Strings status bar to only fill gaps in existing keys)
  4. Lock key+language cells on the Strings page to protect manual edits

Translations are sent in batches (up to 50 texts per DeepL request, grouped by language and context). Protected placeholders are sent as XML tags so DeepL translates whole sentences and can reorder them; rate-limit (429) and server errors are retried automatically.

Other editor menus

Menu Action
Scene View overlay → Language Switch language while editing (Unity 2021.2+)
Assets → DeepLoc → Localization → Split by Locale Creates one LocalizationData asset per language (for Addressables)
GameObject → DeepLoc → Localization → Remove All Localized Components In Hierarchy Bulk-remove localized components

7. Script Scanner

The Scripts page of the DeepLoc Editor finds hard-coded string literals in your C# code (also opened by Tools → DeepLoc → Script Scanner).

What it finds: string literals in .cs files — field initializers, method arguments, $"..." interpolation, etc.

What it skips: strings passed to GetTranslation() or GetTranslations() (already localized), plus everything the preset rules filter out.

Preset rules (scan settings → Rules; all on by default, Recommended restores them, Report everything turns them off). Each one drops strings the player never sees:

Rule Skips
Logs Debug.Log*, print, Console.Write, ...Logger.* calls
PlayerPrefs keys PlayerPrefs.* arguments
Attributes [Tooltip("...")], [Header], [MenuItem], [CreateAssetMenu] …, including attributes split over several lines
Lookup tables dictionary rows { "EN", "English" }, ["EN"] = "English", keys dict["key"]
Names passed to Unity APIs GetComponent, Find, CompareTag, Animator / Material parameters, Shader.Find, Resources.Load, SceneManager, Input, Invoke, SendMessage, reflection, file paths, web request headers/fields
Exceptions and asserts throw new ...Exception("..."), Debug.Assert, Assert.*
Comparisons and string ops == "x", Equals, StartsWith, Contains, Split, Replace, ToString("F2"), case labels
Identifiers, paths, URLs literals without spaces that aren’t words: snake_case, camelCase, lowercase ids, paths, URLs, file names, number formats, hex colors, lone rich-text tags
#if UNITY_EDITOR code code that never ships in a player build

Only UI text (strict) goes further: it reports only literals that are clearly displayed — assignments to .text / title / message / label …, SetText, Show* / Popup / Dialog / Toast / Tooltip calls, new GUIContent(...), title: / message: arguments. Strings stored in a plain variable or array first are not reported in this mode. Add your own popup/dialog API under Display phrases (e.g. PopupManager.Open): statements containing a phrase count as UI text. Display-context strings are ranked High in either mode.

Workflow:

  1. With nothing selected, the panel on the right shows the scan settings: folders to scan, excluded folders, rules (preset rules, display phrases, skip-line regex patterns), and where results are stored (HardcodedStringsData.asset, plus CSV export). Rule changes apply to the next scan.
  2. Click Scan scripts. Results are grouped by file; filter by search text or confidence (High = very likely player-facing).
  3. Select a string to see its code, set its key (existing keys are reused automatically), then Replace in script — the literal becomes GetTranslation("key"), the key is added to Localization Data, and it is translated if DeepL is set up. Add to data only adds the key without touching the script. Ignore hides noise (turn on Show ignored to bring it back).
  4. Select a file row to Replace all in that file, Rescan it, or ignore the whole file. Select several strings to replace or ignore them together.

Replace edits scripts on disk; use version control to revert.

Listed, but no Replace. Some strings are real UI text but must not become GetTranslation(...) calls. They stay in the results, marked · no replace (hover or select for the reason), and only Add to data only is offered:

  • Always: const fields, attribute arguments, case labels, default parameter values (C# needs a compile-time constant there).
  • Field initializers (on by default): string[] tips = { "..." }, static readonly fields, property initializers. They run before LocalizationManager is ready, so GetTranslation would return the key and never follow language changes. Keep the key in the field and translate where the text is shown.
  • Constructors (on by default): instance and static constructors, same timing problem.
  • Editor-only code (on by default): OnValidate, Reset, OnDrawGizmos*, [MenuItem] / [ContextMenu] methods, #if UNITY_EDITOR blocks.

Toggle these under Rules → List, but don’t offer Replace in. Rescan after changing them.

The sample script Sample/Scripts/LocalizationTest.cs contains intentional hardcoded strings for testing the scanner. The showcase scripts in Sample/Scripts/Showcase/ also contain key names and a few literals; exclude the Sample/ folder in the scan settings if you only want your own code.

8. Google Sheets Sync

Keep a Google Sheet in sync with your LocalizationData. Unity only needs a web app URL — no Service Account or Sheets API key.

Setup in Unity

  1. Open Tools → DeepLoc → Editor
  2. Open the Sheets page
  3. Paste your Web App URL
  4. Pick the LocalizationData asset from the toolbar dropdown

Push / Pull

  • Push to Sheet — sends current LocalizationData to the sheet (overwrites sheet)
  • Pull from Sheet — reads the sheet; if anything differs, the changes appear at the top of the Sheets page with a checkbox per item. Added keys and Modified fields (per key and per language) start ticked; keys Only in Unity start unticked and are deleted only if you tick them. Apply selected applies just the ticked items (one Undo step); Discard drops them. Locked translations can’t be ticked
  • Spaces, line breaks and invisible characters are removed from the Web App and Sheet URLs automatically
  • CSV file — offline fallback: Push to Sheet exports a CSV, Pull from Sheet loads one into the same review

Locked languages are respected on import. New language columns in the sheet are added automatically (codes are upper-cased). Blank cells are ignored, so they never erase an existing translation.

Apps Script (one-time setup)

  1. Create a Google Sheet with header row: Key, OriginalText, Context, then language codes (DE, FR, …)
  2. Go to Extensions → Apps Script, paste the script below, and deploy as a Web app
  3. Set SECRET at the top of the script to a random string
  4. Set Execute as = Me, Who has access = Anyone
  5. Copy the deployment URL into Unity and append ?token=<your SECRET>
Apps Script code (click to expand)
// Google Apps Script for Localization Tool Google Sheets live sync.
// Attach this script to your localization Google Sheet (Extensions > Apps Script),
// then deploy it as a Web app and paste the Web App URL into the Unity Localization Tool.
//
// Security: set SECRET to a random string and append ?token=<SECRET> to the Web App URL in Unity.
// Without it, anyone who learns the URL can read or overwrite the sheet.
var SECRET = '';

function getActiveSheet_() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheets = ss.getSheets();
  return sheets[0];
}

function isAuthorized_(e) {
  return !SECRET || (e && e.parameter && e.parameter.token === SECRET);
}

function toCsv_(values) {
  return values.map(function (row) {
    return row.map(function (field) {
      if (field === null || field === undefined) field = '';
      field = String(field);
      if (/[",\r\n]/.test(field)) {
        field = '"' + field.replace(/"/g, '""') + '"';
      }
      return field;
    }).join(',');
  }).join('\n');
}

// GET: return current sheet as CSV (header + rows)
function doGet(e) {
  if (!isAuthorized_(e)) {
    return ContentService.createTextOutput('Unauthorized').setMimeType(ContentService.MimeType.TEXT);
  }
  var sheet = getActiveSheet_();
  // getDisplayValues returns cell text exactly as shown (no Date/number conversion).
  var values = sheet.getDataRange().getDisplayValues();
  var csv = toCsv_(values);

  return ContentService
    .createTextOutput(csv)
    .setMimeType(ContentService.MimeType.CSV);
}

// POST: receive CSV body and overwrite sheet contents
function doPost(e) {
  if (!isAuthorized_(e)) {
    return ContentService.createTextOutput('Unauthorized').setMimeType(ContentService.MimeType.TEXT);
  }
  if (!e || !e.postData || !e.postData.contents) {
    return ContentService
      .createTextOutput('No CSV body')
      .setMimeType(ContentService.MimeType.TEXT);
  }

  var csv = e.postData.contents;
  var rows = Utilities.parseCsv(csv);
  if (!rows || rows.length === 0) {
    return ContentService
      .createTextOutput('Empty CSV')
      .setMimeType(ContentService.MimeType.TEXT);
  }

  // setValues needs a rectangle; pad short rows.
  var width = rows.reduce(function (w, r) { return Math.max(w, r.length); }, 0);
  rows = rows.map(function (r) { while (r.length < width) r.push(''); return r; });

  var sheet = getActiveSheet_();
  sheet.clearContents();
  var range = sheet.getRange(1, 1, rows.length, width);
  // Plain text format so values like "=x", "+5", "1/2", "TRUE" or "001" are not turned into formulas, numbers or dates.
  range.setNumberFormat('@');
  range.setValues(rows);

  return ContentService
    .createTextOutput('OK')
    .setMimeType(ContentService.MimeType.TEXT);
}

9. Addressables (Optional)

Load only the current locale’s data at runtime (e.g. French data when the player selects French).

  1. Install the Addressables package
  2. In DeepLoc Editor → Settings → Addressables, enable Use Addressables and set the address pattern (e.g. LocalizationData_{0})
  3. Click Add Loader to Scene — adds LocalizationAddressablesLoader to the scene
  4. Split by Locale — creates one LocalizationData asset per language
  5. Add those assets to an Addressables group, set addresses to match the pattern, and build

If Addressables keys are missing, the loader falls back to the manager’s single LocalizationData and logs a setup hint. You can disable Use Addressables on the loader to skip this entirely.

10. Sample Scene

Scene location

Sample/Scenes/DeepLocShowcase.unity

Run it

  1. Open the scene and press Play
  2. Use the top bar:
    • Language — switches every text, image, sound and prefab in the scene
    • Platform — previews per-platform text (e.g. “Click” on desktop, “Tap” on mobile, “Press Ⓐ” on console)
    • Pseudo — turns on pseudo-localization to spot untranslated or overflowing text
  3. Browse the pages on the left rail. Every card lists the keys it uses at the bottom, and a Try it in the Editor box points to the matching page of the DeepLoc Editor.

The status strip at the bottom shows the latest OnLanguageChanged event.

Sample data

The scene’s LocalizationManager uses Sample/Data/SampleLocalizationData.asset, not your project’s Data/LocalizationData.asset, so the demo never adds keys to your own data. All sample keys start with Sample..

  • Languages: EN (source), FR, DE, ES, HI, AR, ZH — fully translated, no DeepL key needed
  • To inspect or edit it, open Tools → DeepLoc → Editor, then choose the sample asset from the data selector in the toolbar
  • Deleting the Sample/ folder removes the scene, its data and every sample asset without touching your project

What each page demonstrates

Page Features
Welcome Language buttons built from LocalizationData, current language with a localized sprite
Text Rich-text tags, {0} placeholders via GetTranslationFormat, TextBackgroundScaler, missing-translation placeholder (N/A_<Key>_<LANG>)
Assets LocalizedImage, LocalizedAudioSource (placeholder chimes — swap in real voice lines), LocalizedPrefab (per-language HUD with English fallback)
Right-to-left The same key in English and Arabic, rich text in Arabic, Western vs Arabic-Indic digits (PreserveRtlNumbers)
Plurals Count slider; each language picks its CLDR plural form (GetTranslation(key, count))
Platforms Platform overrides for one key, resolved for every LocPlatform
Code API GetTranslation, GetAsset<T> and OnLanguageChanged with live results
Editor tools Targets for the Scan page (two deliberately unlocalized labels and one IgnoreLocalization label) and steps for Scripts, DeepL, Sheets, Fonts and Addressables

Sample scripts

Script Role
Showcase/ShowcaseNavigation.cs Left-rail page switching
Showcase/ShowcaseDynamicText.cs Script-driven text: fixed language, format arguments, plural count or platform
Showcase/ShowcaseLanguageGrid.cs Language buttons with native names
Showcase/ShowcasePlatformSelector.cs, ShowcasePseudoToggle.cs, ShowcaseDigitsToggle.cs Top-bar and RTL controls
Showcase/ShowcaseCodeDemo.cs The calls behind the Code API page
Showcase/ShowcaseTheme.cs, ThemedGraphic.cs Color theme (Sample/UI/ShowcaseTheme.asset)
LocalizationTest.cs Intentional hardcoded strings for Script Scanner testing (not in the scene)

11. Troubleshooting

Problem Fix
Translations not showing Check the key on LocalizedText; click Refresh scene text on the Home page
Text shows N/A_Key_LANG Add the missing translation in LocalizationData
Language not switching Assign LocalizationData to LocalizationManager
Addressables errors in console Disable Use Addressables on the loader, or complete Split by Locale setup
Arabic / rich text corrupted Enable Variable Protection, re-translate the entry
Missing glyphs (boxes/squares) Run Fix Fonts and Validate Glyphs on the Fonts page
Hindi vowel signs in the wrong place (e.g. “ि” after its consonant) or conjuncts with a visible virama Click Bake conjuncts on the Fonts page, and show the text through LocalizedText or DisplayTextShaper (see Devanagari in Section 5)
API key not working Use Test API Key in DeepLoc Editor

File Reference

Path Purpose
Data/LocalizationData.asset Main localization data
Data/HardcodedStringsData.asset Script scanner results
Prefabs/LocalizationManager.prefab Drop into any scene
Sample/Scenes/DeepLocShowcase.unity Sample scene
Sample/Data/SampleLocalizationData.asset Sample data (7 languages, Sample.* keys)
Core/Generated/LocKeys.cs Optional key constants (Generate LocKeys)