Pinned by Arystus on 2026-08-09Locked

Mod Guide EN

Arystus · 3 days ago

Using MultiLangLib in your own mod

This guide explains how to add MultiLangLib as a shared translation library to a Captain of Industry mod. The consumer mod in the examples is named MyMod.

1. Declare the dependency

Add MultiLangLib to your mod's manifest.json. The dependency deliberately contains no spaces around >=:

{
  "id": "MyMod",
  "version": "1.0.0",
  "primary_dlls": [ "MyMod.dll" ],
  "mod_dependencies": [ "MultiLangLib>=0.1.0" ]
}

This ensures that your mod is only loaded when a compatible MultiLangLib version is available.

2. Reference the DLL

Reference the installed MultiLangLib.dll in your project file, but do not copy it to your mod's output directory:

<ItemGroup>
  <Reference Include="MultiLangLib">
    <HintPath>$(APPDATA)\Captain of Industry\Mods\MultiLangLib\MultiLangLib.dll</HintPath>
    <Private>false</Private>
  </Reference>
</ItemGroup>

Private=false is important: exactly one shared MultiLangLib DLL should be loaded. If both projects are in the same source tree, you can use a project reference with Private="false" instead.

3. Register the mod directory

Import the namespace and register your mod's root directory in its constructor:

using MultiLangLib;
using Mafi.Core.Mods;

public sealed class MyMod : DataOnlyMod {

    public MyMod(ModManifest manifest) : base(manifest) {
        Lang.RegisterMod(manifest.Id, manifest.RootDirectoryPath);
    }

    // Remaining mod implementation ...
}

Registration is recommended. MultiLangLib can discover normally installed sibling directories automatically, but explicit registration also works reliably with nonstandard directory layouts.

4. Create language files

Store the translations in your own mod's lang directory:

MyMod\
├── MyMod.dll
├── manifest.json
└── lang\
    ├── de.json
    └── en.json

The recommended format is a simple JSON object. en.json:

{
  "window.title": "Production overview",
  "window.close": "Close",
  "welcome": "Welcome, Captain {0}!"
}

And de.json:

{
  "window.title": "Produktionsübersicht",
  "window.close": "Schließen",
  "welcome": "Willkommen, Captain {0}!"
}

The COI array format is also accepted:

[
  [ "multilanglib.MyMod.window.title", "Production overview" ],
  [ "window.close", "Close" ]
]

Both short IDs and complete keys are supported. Use each ID only once within a file.

5. Resolve text in code

Every complete key follows this pattern:

multilanglib.<ModId>.<TextId>

Typical calls:

using Mafi.Localization;
using MultiLangLib;

string title = Lang.Get("multilanglib.MyMod.window.title");
string close = Lang.Get("MyMod", "window.close");
LocStrFormatted titleForUi = Lang.Localized("MyMod", "window.title");
string greeting = Lang.Format("multilanglib.MyMod.welcome", playerName);
  • Lang.Get(...) returns an already resolved string.
  • Lang.Localized(...) wraps the resolved text in LocStrFormatted for COI user interfaces.
  • Lang.Format(...) replaces placeholders using the active language culture.
  • Lang.TryGet(...) reports through its return value whether an entry was found.

A plain string such as "multilanglib.MyMod.window.title" is not replaced automatically by the game. Always resolve the key through the MultiLangLib API.

Key rules

  • The ModId must begin with a letter or digit.
  • The remainder of the ModId may contain letters, digits, _, and -.
  • The TextId may additionally contain dots, for example settings.audio.volume.
  • Dots are not allowed in the ModId.
  • IDs are case-sensitive.

A static helper class prevents typing mistakes at call sites:

public static class Texts {

    public static string WindowTitle =>
        Lang.Get("MyMod", "window.title");

    public static LocStrFormatted WindowTitleForUi =>
        Lang.Localized("MyMod", "window.title");
}

Lookup order and fallback

For multilanglib.MyMod.window.title with German selected in the game, MultiLangLib searches in this order:

1. <MyMod>/lang/de.json
2. <MultiLangLib>/lang/MyMod/de.json
3. <MyMod>/lang/en.json
4. <MultiLangLib>/lang/MyMod/en.json
5. multilanglib.MyMod.window.title

Central files under MultiLangLib\lang\MyMod\ can be used for community translations supplied separately, for example. The file included in the consumer mod takes precedence.

For regional languages, the exact variant is checked before the neutral variant, such as de-DE.json before de.json. In automatic mode, MultiLangLib uses the file names supplied by Captain of Industry, including names such as pt_BR.json and zh_Hans.json.

Debugging

Enable the following option in the MultiLangLib settings:

debug_language = true

Every valid lookup will then return its complete key. This makes it easy to identify which translation is expected at any location in the UI.

Additional settings:

  • language_override = auto: follows the language selected in the game.
  • language_override = debug: also enables key output.
  • language_override = de: forces a language.
  • language_override = de.json: forces a literal file name.
  • fallback_language = en: selects the fallback language.

MultiLangLib caches loaded files. During development, call Lang.Reload() after changing a file. UI elements that have already been rendered are not rebuilt automatically.

Common problems

Only the key is visible in the game: Check the file name, ModId, TextId, and capitalization. Also verify that the JSON is valid and MultiLangLib is enabled.

The consumer mod does not load: Check the MultiLangLib>=0.1.0 dependency and confirm that the library directory is named MultiLangLib.

There is a DLL conflict: Set Private=false on the reference and do not distribute MultiLangLib.dll inside the consumer mod directory.

Placeholders remain visible: Use Lang.Format(...) and supply a suitable argument for every placeholder. Format errors are logged; MultiLangLib safely returns the unformatted text in that case.

A changed file is not reloaded: Call Lang.Reload() during development or restart the game.

Complete example

A buildable example containing a manifest, project reference, C# calls, and German and English language files is available under:

examples/ExampleConsumer/
9
Showing 1–1 of 1