验证环境
本页示例已使用 Build 740622 对应的本机 Managed DLL 完成编译检查;尚未在游戏内完成运行验证。
翻译文本与本地化
Mod 里的文本不要散在各个类里。建筑名、物品说明、按钮文字、效果描述都应该集中放到 STRINGS,再用翻译文件覆盖不同语言。
这样做有三个好处:
- 文本路径稳定,游戏 UI 更容易找到对应名称和描述。
- 以后改文案不用翻遍代码。
- 需要英文、中文或其他语言时,只改
.po文件。
推荐结构
一个简单 Mod 可以这样放:
MyMod/
├── MyMod.dll
├── mod.yaml
└── translations/
└── en.poSTRINGS.cs 跟源码一起编译进 dll。translations 目录要复制到最终 Mod 目录,和 dll 放在同一层。
定义 STRINGS
普通文本优先用 LocString,不要直接写 string。当前可编译的字符串声明位于 examples/Localization/STRINGS.cs:
namespace ONITutorial.Localization
{
public static class STRINGS
{
public static class UI
{
public static class TUTORIAL
{
public static LocString TITLE = "ONI Tutorial Localization";
public static LocString DESCRIPTION = "A string registered through the current localization flow.";
}
}
}
}类名通常用大写 ID。比如建筑 ID 是 MyBuilding,文本路径里常写成 MYBUILDING。
注册本地化
常见做法是在 Localization.Initialize 后注册 STRINGS,然后加载当前语言的 .po。完整可编译示例位于 examples/Localization/Mod.cs:
using System.IO;
using System.Reflection;
using HarmonyLib;
using KMod;
using UnityEngine;
namespace ONITutorial.Localization
{
public sealed class Mod : UserMod2
{
public override void OnLoad(Harmony harmony)
{
base.OnLoad(harmony);
Debug.Log("[ONITutorial.Localization] Loaded");
}
[HarmonyPatch(typeof(global::Localization), nameof(global::Localization.Initialize))]
private static class LocalizationInitializePatch
{
private static void Postfix()
{
global::Localization.RegisterForTranslation(typeof(STRINGS));
LocString.CreateLocStringKeys(typeof(STRINGS), null);
string modDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string localeCode = global::Localization.GetLocale()?.Code ?? "en";
string poFile = Path.Combine(modDirectory, "translations", localeCode + ".po");
if (File.Exists(poFile))
global::Localization.OverloadStrings(global::Localization.LoadStringsFile(poFile, false));
}
}
}
}这段代码按固定顺序完成三件事:
RegisterForTranslation会按STRINGS.前缀为当前程序集里的LocString生成可翻译路径。LocString.CreateLocStringKeys为运行时字符串创建游戏使用的 key。OverloadStrings用当前语言文件覆盖默认文本。
生成翻译模板
开发时可以生成模板,方便知道有哪些路径需要翻译:
string modPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
Localization.GenerateStringsTemplate(
typeof(STRINGS),
Path.Combine(modPath, "translations"));这句适合临时打开,用来生成或更新模板。正式发布前建议关掉,避免每次进游戏都改动翻译目录。
生成出来的模板是最可靠的路径来源。手写 .po 时,优先对照模板,不要凭记忆拼路径。
编写 po 文件
.po 文件大致长这样:
msgctxt "STRINGS.BUILDINGS.PREFABS.MYBUILDING.NAME"
msgid "我的建筑"
msgstr "My Building"
msgctxt "STRINGS.BUILDINGS.PREFABS.MYBUILDING.DESC"
msgid "一个用于演示本地化的建筑。"
msgstr "A building used to demonstrate localization."
msgctxt "STRINGS.UI.MYBUILDING.TITLE"
msgid "建筑设置"
msgstr "Building Settings"msgctxt 是文本路径,msgid 是代码里的默认文本,msgstr 是当前语言要显示的文本。
如果某条 msgstr 留空,游戏通常会回到默认文本。排查翻译不生效时,先看路径是否完全一致。
常见文本路径
建筑:
STRINGS.BUILDINGS.PREFABS.MYBUILDING.NAME
STRINGS.BUILDINGS.PREFABS.MYBUILDING.DESC
STRINGS.BUILDINGS.PREFABS.MYBUILDING.EFFECT物品:
STRINGS.ITEMS.INDUSTRIAL_PRODUCTS.MYITEM.NAME
STRINGS.ITEMS.INDUSTRIAL_PRODUCTS.MYITEM.DESC食物:
STRINGS.ITEMS.FOOD.MYFOOD.NAME
STRINGS.ITEMS.FOOD.MYFOOD.DESC植物:
STRINGS.CREATURES.SPECIES.MYPLANT.NAME
STRINGS.CREATURES.SPECIES.MYPLANT.DESC
STRINGS.CREATURES.SPECIES.MYPLANT.DOMESTICATEDDESC种子:
STRINGS.CREATURES.SPECIES.SEEDS.MYPLANTSEED.NAME
STRINGS.CREATURES.SPECIES.SEEDS.MYPLANTSEED.DESC元素:
STRINGS.ELEMENTS.MYELEMENT.NAME
STRINGS.ELEMENTS.MYELEMENT.DESC效果:
STRINGS.DUPLICANTS.MODIFIERS.MYEFFECT.NAME
STRINGS.DUPLICANTS.MODIFIERS.MYEFFECT.TOOLTIP特质:
STRINGS.DUPLICANTS.TRAITS.MYTRAIT.NAME
STRINGS.DUPLICANTS.TRAITS.MYTRAIT.DESC自定义 UI 可以放在自己的命名空间下:
STRINGS.UI.MYBUILDING.TITLE
STRINGS.UI.MYBUILDING.ENABLE
STRINGS.UI.MYBUILDING.DISABLE在代码里使用文本
如果是游戏原生会读取的字段,只要路径对上,通常不用手动取文本。比如建筑配置里:
BuildingDef def = BuildingTemplates.CreateBuildingDef(
ID,
width,
height,
anim,
hitpoints,
construction_time,
construction_mass,
construction_materials,
melting_point,
build_location_rule,
decor,
noise);建筑名、描述和效果会按 ID 去找:
STRINGS.BUILDINGS.PREFABS.MYBUILDING.NAME
STRINGS.BUILDINGS.PREFABS.MYBUILDING.DESC
STRINGS.BUILDINGS.PREFABS.MYBUILDING.EFFECT自定义 UI 里需要手动赋值时,可以直接引用 LocString:
label.text = STRINGS.UI.MYBUILDING.TITLE;也可以用路径取值:
label.text = Strings.Get("STRINGS.UI.MYBUILDING.TITLE");项目里保持一种写法就好。UI 文本多的时候,直接引用 STRINGS 更容易查找。
动态文本
少量动态路径可以用 Strings.Add:
Strings.Add("STRINGS.UI.MYMOD.CONFIRM", "确认");它适合运行时生成的键,或者很难写进静态 STRINGS 的内容。普通建筑、物品、按钮、效果文本仍然建议放进 LocString。
动态文本也要注意加载顺序。如果 UI 已经创建完才 Strings.Add,旧 UI 不一定会自动刷新。
编译和复制文件
确认发布目录里有这些东西:
MyMod/
├── MyMod.dll
├── mod.yaml
└── translations/
├── zh.po
└── en.po如果用 Visual Studio 或 Rider,可以把 .po 设置为复制到输出目录。也可以在打包脚本里把 translations 整个目录复制过去。
最简单的测试方式:
- 进游戏确认默认语言能显示文本。
- 切换语言后重启游戏。
- 看建筑名、描述、按钮、效果说明是否都变成目标语言。
- 打开日志,搜索缺失路径或
.po加载失败信息。
常见坑
不要把显示文本直接写在组件逻辑里。以后做翻译时会很难找,也容易漏。
不要混用 MYBUILDING、MY_BUILDING、MyBuilding。ID、类名、路径要提前定好规则。
不要只翻译 NAME,忘了 DESC 和 EFFECT。建筑卡片里这三项都会被玩家看到。
不要把 .po 放进源码目录后忘记复制到 Mod 目录。游戏只能加载运行时目录里的文件。
不要在公开教程或示例里写本机路径、个人昵称、私有项目名。示例保持通用,读者照着改 ID 就能用。