🌐 English | Українська
FEATURES | QUICK START | TOOLTIP | CSV FORMAT | FONTS | API | DEMO SCENE | NOTES | LICENSE | SUPPORT
A simple CSV-based text localization system for Unity projects using TextMeshPro. Reads a CSV file, returns the rows, switches fonts, and caches everything for optimization. Made for solo devs and indie teams who want to keep text strings out of scripts and manage translations through external editors.
Source: This is a simplified version of the localization system from Automatic Tutorial Maker asset.
CSV-based translations
Edit in Excel, Google Sheets, or any CSV editor. Default: ; delimiter, UTF-8, " text qualifier.
12+ languages included
English, Chinese, Japanese, German, French, Spanish, Portuguese, Italian, Ukrainian, Polish, Turkish, Korean. Add custom via Language enum or column name.
Automatic font switching
Assign fonts per language (e.g., NotoSansTC for Chinese), auto-applies on change. Includes Noto Sans CJK + Audex fonts with licenses.
Dynamic placeholders
Insert variables: {playerName}, {score}, {anyVariable} → filled at runtime.
TextMeshPro tags support
Use <color=#FF0000>, <sprite=0>, emojis 😊 directly in translations.
Two usage modes:
InputData asset in Assets/CSV Localization/InputDataNote: You can create your own InputData asset (
Right-click → Create → Localization System Mini → InputData) and CSV file if needed.
using LocalizationSystemMini;
public class GameManager : MonoBehaviour
{
[SerializeField] private InputStringsScriptableObject textStrings;
void Start()
{
// Initialize localization system (loads saved language or sets default)
textStrings.InitializeLocalizator();
}
}
In the Inspector → Button component → OnClick():
English, Japanese, Ukrainian, etc.Row Indexing: Visual editors (Excel, Google Sheets) start from row 1, but code uses 0-based indexing. When specifying a row number, subtract 1 from what you see in the editor (e.g., row 2 in Excel = index 1 in code). Alternatively, use keys instead of row numbers. This is more reliable and allows you to swap table rows.
Static Mode – caches text objects, auto-updates on language change, handles font switching
Call once: on Start, or when spawning text objects
void Start()
{
// Simple text
textStrings.FillTextObject(2, textComponent);
// By key (uses column A)
textStrings.FillTextObjectByKey("settings_key", textComponent);
// With placeholders (requires manual re-call if variables change)
textStrings.FillTextObjectWithPlaceholders(2, textComponent, userName, score);
textStrings.FillTextObjectWithPlaceholdersByKey("row_key", textComponent, userName, score);
}
Dynamic Mode – reads on-demand, no font switching
Call in Update() or when data changes frequently
void Update()
{
// By row index
textComponent.text = textStrings.GetStringText(2);
// By key
textComponent.text = textStrings.GetStringTextByKey("greeting_key");
// With placeholders
textComponent.text = textStrings.ReplacePlaceholders(3, playerName, level);
textComponent.text = textStrings.ReplacePlaceholdersByKey("row_key", playerName, level);
// Manual font update (call once, if needed)
textComponent.font = textStrings.GetCurrentFont();
}
Change Language – bind to UI buttons for language switching
// By enum
textStrings.ChangeLanguage(InputStringsScriptableObject.Language.Japanese);
// By string (recommended for custom languages)
textStrings.ChangeLanguage("Ukrainian");
A component for automatic text localization directly on scene objects without writing code.
For text with variables, use Parameter Selector:
Parameter Updates:
Update() method in LocalizedTooltip.csFillTextObject() after value changes| A | B | C | D | E | F | … | |
|---|---|---|---|---|---|---|---|
| 1 | Use keys | Default | English | Chinese | Japanese | German | … |
| 2 | greeting_key | hello | hello | 你好 | こんにちは | hallo | … |
| 3 | welcome_key | Welcome, {name}! | Welcome, {name}! | 欢迎,{name}! | ようこそ、{name}! | Willkommen, {name}! | … |
| 4 | yes_key | Yes | Yes | 是 | はい | Ja | … |
GetStringTextByKey){variableName} syntax. Position in sentence can vary between languages, but order must be identical in all translations.Important: Placeholders are replaced by position, not by name. The system replaces the 1st placeholder with the 1st value, 2nd with 2nd value, etc., regardless of what’s inside the braces.
Example with call:
ReplacePlaceholders(row, "John", 100)✅ Correct:
EN:"Player {name} has {score} coins"→ “Player John has 100 coins”
UA:"У гравця {name} є {score} монет"→ “У гравця John є 100 монет”
(Same order: {name} first, {score} second)❌ Wrong:
UA:"{score} монет у гравця {name}"→ “John монет у гравця 100” (Reversed order: system replaces {score} with “John”, {name} with 100)
For languages with special characters (Chinese, Japanese, Korean):
ChineseNotoSansTC-Regular SDFThe system automatically switches fonts when language changes.
| Method | Description | Use Case |
|---|---|---|
InitializeLocalizator() |
Load saved language or set default | Call once on game start |
ChangeLanguage(Language) |
Switch to enum language | Testing in Inspector |
ChangeLanguage(string) |
Switch by string name | UI language buttons, custom languages |
GetStringText(int row) |
Get text by row | Dynamic reading |
GetStringTextByKey(string key) |
Get text by key | Dynamic reading |
FillTextObject(int row, TMP_Text) |
Cache text object | Static UI |
ReplacePlaceholders(int row, params object[]) |
Get text with variables | Dynamic with variables |
FillTextObjectWithPlaceholders(int row, TMP_Text, params object[]) |
Cache text with variables | Static with variables |
ReplacePlaceholdersByKey(string key, params object[]) |
Get text with variables | Dynamic with variables |
FillTextObjectWithPlaceholdersByKey(string key, TMP_Text textToFill, params object[]) |
Cache text with variables | Static with variables |
GetCurrentFont() |
Get current language font | Manual font management |
_currentLanguageString – Current language as string. Track this to detect language changes in external scripts (e.g., if (language != textStrings._currentLanguageString)).
Open DemoScene to see all features in action.
TextCallExamples componentExamine TextCallExamples.cs for implementation patterns:
Saved language
Stored in Application.persistentDataPath/LocalizationFolder/language_global.json
Use the “Open Saved Language File” button in the InputData Inspector to navigate to this folder.
Language enum in InputStringsScriptableObject.csChangeLanguage(string) without modifying the enumFree to use in personal and commercial projects.
For the full-featured version, visit: Automatic Tutorial Maker on Unity Asset Store
This system is used for localizing text tooltips, can additionally find a value by the closest match (without relying on a row number or key), and can export auto-generated or manually created texts to CSV from a Scriptable Object. The storage contains all built-in tooltips as well as additional texts created across all scenes and tutorial steps in the Unity project.
P.S. There’s already a solution based on CSV: Localization Package from Unity, which includes most of the possible features. This system shows a simpler version for indie projects, with some unique advantages. For instance, it can cache a pair of TMP_Text and its string, so that when a translation is updated, all pairs are refreshed automatically, including the font. It supports languages with any name (you can add a column like “Elvish” and reference it by “Elvish”). And it uses only one ScriptableObject as a “middleman”, accessible from anywhere, so you don’t need to deal with a bunch of components.
Dev’s website: https://www.octantastudio.com/
Gmail: octantastudio@gmail.com
Discord: https://discord.gg/6SPxKpFZFC
| FEATURES | QUICK START | TOOLTIP | CSV FORMAT | FONTS | API | DEMO SCENE | NOTES | LICENSE | SUPPORT |