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:

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

  1. Launch the Unity Editor or a built executable.
  2. Navigate to Settings → Language Pack.
  3. Select your new language from the list; the UI should immediately refresh using your Setting.txt values.

If the language fails to appear:

  • Confirm LanguagePackInformation.txt contains a non-empty LanguagePackName.
  • Verify the folder resides in the correct path (Assets/LanguagePacks/ for external, TextAssets/LanguagePacks/ for internal).
  • Check the Unity console for DataHandlerService IO exceptions or missing key errors from TextProcessingService.

Summary

  • External packs drop into Assets/LanguagePacks/ and require no code changes; the engine discovers them via Directory.GetDirectories.
  • Internal packs require incrementing MainControl.LanguagePackageInternalNumber and updating DataHandlerService.GetLanguageInsideId to map new indices.
  • Every pack needs LanguagePackInformation.txt at the root, plus UI/ and Scene/ subfolders containing .txt key-value files.
  • The UI renders language names using SettingsController.GetLanguagePacksName, which parses metadata through DataHandlerService.LoadItemData.
  • Ink dialogue support requires an optional Ink/ folder with .ink and 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →