How to Add Support for New Languages in the Undertale Changer Template
To add a new language to the Undertale Changer Template, copy an existing language pack folder into Assets/LanguagePacks/ for external (user-added) packs or TextAssets/LanguagePacks/ for internal (built-in) packs, rename it, populate the LanguagePackInformation.txt metadata file and the text assets under UI/ and Scene/, then verify it appears in Settings → Language Pack without modifying core game logic.
The Undertale Changer Template by arch-aik uses a data-driven localization system that loads translation assets at runtime. Whether you are creating a fan translation or building a multi-language mod, you can add new language support by providing properly structured text files in the correct directory hierarchy. This article details the exact folder layout, configuration files, and service methods—such as DataHandlerService.LoadLanguageData—that enable seamless language integration.
Language Pack Architecture: Internal vs. External
The template distinguishes between two pack types based on their location in the project hierarchy.
Internal packs reside in TextAssets/LanguagePacks/ and compile directly into the Unity build. The engine expects exactly MainControl.LanguagePackageInternalNumber (default: 3) internal packs, mapped to IDs 0–2 via DataHandlerService.GetLanguageInsideId.
External packs live in Assets/LanguagePacks/ and load from the host file system at runtime. The engine discovers these automatically using Directory.GetDirectories, requiring no code changes to add new languages.
For rapid iteration and distribution, external packs are recommended; internal packs are reserved for translations that must ship with the executable on platforms without file-system access.
Creating an External Language Pack
External packs require zero C# modifications. Follow these steps to register a new language.
Copy an Existing Template
Duplicate one of the existing pack folders—such as US_Template—into the external directory:
cp -r Assets/LanguagePacks/US_Template Assets/LanguagePacks/JP_Template
Rename the folder to your desired language identifier (e.g., JP_Template). This name appears in the Settings menu and serves as the pack’s unique handle.
Configure LanguagePackInformation.txt
Inside the pack root, create or edit LanguagePackInformation.txt. This metadata file drives the UI display in SettingsController.GetLanguagePacksName. Use the following key-value format:
LanguagePackName=Japanese
LanguagePackAuthor=Your Name
LanguagePackInformation=Japanese translation for the template.
LanguagePackFullWidth=False
CultureInfo=ja-JP
The SettingsController reads these values via DataHandlerService.LoadItemData, which calls TextProcessingService.GetFirstChildStringByPrefix to extract each field.
Translate UI Text Files
Create the UI/ subdirectory and populate these required text files:
Setting.txt– Menu labels and option names.ItemText.txt– Item descriptions and inventory strings.
Each file contains key-value pairs, one per line:
LanguagePack=言語パック
LanguageBack=戻る
Open=開く
Close=閉じる
SettingsController.UpdateLanguagePacksConfigDisplay (lines 800–822) references these keys when rendering the settings interface.
Translate Scene Text
Under Scene/, replicate the file structure from the template (e.g., Story.txt, Menu.txt, Battle.txt). The engine loads these via DataHandlerService.LoadLanguageData when the player enters the corresponding scene.
Title=はじまりの物語
Start=ゲーム開始
Optional: Add Ink Dialogue Scripts
If your narrative uses Ink, create an Ink/ subfolder containing InkExample.ink and its compiled JSON. TypeWritterTagProcessor loads these scripts when the active languagePackId matches your pack index (lines 593–603).
Creating an Internal Language Pack
To embed a language directly into the build, place the pack folder under TextAssets/LanguagePacks/ instead of Assets/LanguagePacks/. This requires two code modifications in Assets/Scripts/UCT/Core/MainControl.cs and Assets/Scripts/UCT/Service/DataHandlerService.cs.
Update the Internal Pack Count
Open MainControl.cs and increment LanguagePackageInternalNumber to reflect the new total:
public static int LanguagePackageInternalNumber => 4; // Increased from 3
Source: MainControl.cs (line 115).
Map the New Language ID
Extend the switch expression in DataHandlerService.GetLanguageInsideId to assign a two-letter code to the new index:
public static string GetLanguageInsideId(int id)
{
return id switch
{
0 => "CN",
1 => "TCN",
2 => "US",
3 => "JP", // Add this line for the new internal language
_ => "US"
};
}
Source: DataHandlerService.cs (line 78).
The engine now treats index 3 as the internal Japanese pack, loading it from Resources.Load instead of the file system.
How the Engine Loads Language Data
Understanding the loading mechanism helps debug missing translations. The DataHandlerService.LoadLanguageData method (line 307) branches based on the language ID:
public static string LoadLanguageData(string path, int id)
{
return id < MainControl.LanguagePackageInternalNumber
? Resources.Load<TextAsset>($"TextAssets/LanguagePacks/{GetLanguageInsideId(id)}/{path}").text
: File.ReadAllText($"{Directory.GetDirectories(Application.dataPath + "/LanguagePacks")[id - MainControl.LanguagePackageInternalNumber]}\\{path}.txt");
}
For IDs less than LanguagePackageInternalNumber, the method pulls from compiled Resources; for higher IDs, it calculates an offset into the external directory array and reads from disk.
Verifying the New Language
- Launch the Unity Editor or a built executable.
- Navigate to Settings → Language Pack.
- Select your new language from the list; the UI should immediately refresh using your
Setting.txtvalues.
If the language fails to appear:
- Confirm
LanguagePackInformation.txtcontains a non-emptyLanguagePackName. - Verify the folder resides in the correct path (
Assets/LanguagePacks/for external,TextAssets/LanguagePacks/for internal). - Check the Unity console for
DataHandlerServiceIO exceptions or missing key errors fromTextProcessingService.
Summary
- External packs drop into
Assets/LanguagePacks/and require no code changes; the engine discovers them viaDirectory.GetDirectories. - Internal packs require incrementing
MainControl.LanguagePackageInternalNumberand updatingDataHandlerService.GetLanguageInsideIdto map new indices. - Every pack needs
LanguagePackInformation.txtat the root, plusUI/andScene/subfolders containing.txtkey-value files. - The UI renders language names using
SettingsController.GetLanguagePacksName, which parses metadata throughDataHandlerService.LoadItemData. - Ink dialogue support requires an optional
Ink/folder with.inkand JSON assets.
Frequently Asked Questions
Can I add a new language without recompiling the Unity project?
Yes. Place your translation folder in Assets/LanguagePacks/ (external packs). The DataHandlerService scans this directory at runtime using Directory.GetDirectories, making the language available immediately without touching MainControl.LanguagePackageInternalNumber or any C# scripts.
What files are mandatory for a language pack to appear in the Settings menu?
At minimum, the pack folder must contain LanguagePackInformation.txt with a defined LanguagePackName key. Without this value, SettingsController.GetLanguagePacksName cannot populate the dropdown. Additionally, include UI/Setting.txt to ensure the settings interface itself has translated labels.
How does the engine decide whether to load a pack from Resources or from disk?
The decision happens in DataHandlerService.LoadLanguageData. If the requested language ID is less than MainControl.LanguagePackageInternalNumber, the engine calls Resources.Load on TextAssets/LanguagePacks/; otherwise, it calculates an external index and reads from Application.dataPath + "/LanguagePacks" using File.ReadAllText.
What is the difference between LanguagePackFullWidth and CultureInfo in the metadata?
LanguagePackFullWidth is a boolean flag that toggles full-width text rendering for languages like Chinese or Japanese, read by the UI layout system. CultureInfo specifies the .NET culture code (e.g., ja-JP) used for number, date, and currency formatting during localization parsing. Both are optional keys in LanguagePackInformation.txt.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →