从 2.5.0.0 开始,配置可以通过语言包别名和字段路径引用翻译。配置负责功能与结构,语言文件负责文本,不需要为每种语言复制整份配置。
本文介绍引用的写法和取值规则。LinView 的显示字段已经支持这些写法;要在插件自己的配置中使用语言引用,请见配置文本 ⇱。普通 String 字段不会自动解释引用。
先为语言对象绑定一个别名:
MainLanguage lang = lin.linFile().language().bind("main", MainLanguage.class);
main 是当前语言服务内的别名,不是目录名。资源目录仍由 @LangPack 决定:
@LangPack(filePath = "messages", defaultLocale = "zh_CN")
@NamingStyle(NamingStyle.Style.KEBAB)
public class MainLanguage {
public Notice notice = new Notice();
public static class Notice {
public LangText title = LangText.of("操作结果");
public LangText successMessage = LangText.of("{name},操作已完成");
public LangList details = LangList.of("数据已保存", "可以继续下一步操作");
}
}
对应的 Jar 资源为 langservice/messages/zh_CN.yml,生成到当前插件数据目录的 messages/zh_CN.yml:
notice:
title: "操作结果"
success-message: "{name},操作已完成"
details:
- "数据已保存"
- "可以继续下一步操作"
别名只在当前插件的语言服务中有效。不同插件使用相同别名不会互相影响,同一插件也可以绑定多个语言包。别名区分大小写,只允许字母、数字、下划线和连字符。
重复绑定同一类会复用语言对象;将已有别名绑定到另一类会抛出 IllegalArgumentException。
写回控制仍然可用:bind("main", MainLanguage.class, false)。带别名的绑定与普通绑定共享记录,输出策略仍以该类首次绑定时的设置为准。
在支持语言引用的字段中,普通文本与语言引用可以直接替换:
heading: "操作结果"
heading: "@lang(main:notice.title)"
括号内为 别名:包内路径。路径使用语言文件实际的键名,层级之间用点号分隔。例如,经过 KEBAB 命名转换的字段应写为 notice.success-message,而不是 Java 字段名 notice.successMessage。路径各段允许字母、数字、下划线和连字符。
YAML 中请给引用加上引号,因为 @ 不能作为未加引号的值的起始字符。JSON 中也可以使用相同的字符串写法。
如果字段需要一组文本,可以引用语言文件中的整个字符串列表:
description: "@lang(main:notice.details)"
heading 和 description 是示例字段名,并非 LinFile 预设配置项。字段需要字符串还是列表,由读取配置的模块或 Java 字段类型决定。
单行引用等价于只声明 lang 的对象形式:
heading:
lang: "main:notice.title"
需要指定回退值时,再添加 fallback:
heading:
lang: "main:notice.title"
fallback: "操作结果"
description:
lang: "main:notice.details"
fallback: []
查找沿用语言服务已有的资源与代码默认值补齐规则;路径仍不存在时,会查询该包的默认语言。别名未绑定、最终未找到值或值的类型不符时,使用配置中的回退值。
单段文本必须引用字符串,列表必须引用字符串列表,不会自动把列表拼接为字符串。回退值也必须符合字段类型,空字符串和空列表都是有效值。
没有声明 fallback 时,单段文本使用 [main:notice.title] 这样的标记,列表使用包含该标记的一行列表,以便定位遗漏。
只有完整的 @lang(...) 字符串或明确的 lang 对象才表示引用。"main:notice.title" 是普通文本,不会触发查询。不支持语言引用的字段也不会自动解释引用,例如普通的文件路径、材质 ID 和命令参数。
这不是字符串拼接语法。需要“欢迎来到商店”这样的句子时,应将完整句子放入语言文件,再由使用文本的模块替换变量,不要在一句话中嵌入语言引用。
以 @lang( 开头的值会按引用检查;缺少右括号、路径不合法或后面带有其他内容,都属于格式错误。其他位置出现的 @lang(...) 保持字面含义。普通字符串列表不会逐项展开引用;Java 中的 List<ConfigText> 则会按元素类型解析,详见配置文本 ⇱。
语言引用只负责选择文本,不递归解析翻译内容中的其他引用,也不提供业务变量。语言文件中的 {name}、颜色和高级字符串效果,仍由最终使用文本的模块处理。
引用格式、未知选项或回退类型错误属于配置结构错误。通过配置服务绑定时,会参与配置校验 ⇱;自行调用解析入口时,格式错误会抛出 IllegalArgumentException。
运行时缺少翻译或翻译类型不符,则使用回退内容,并通过 LIN-FILE-LANGUAGE-REFERENCE-FAIL 报告。同一解析器会对相同引用及期望类型的错误去重,避免每次取值都重复记录。
LinView 的 title、图标 name 和整段 lore 已接入语言引用,适用于静态控件、动态区模板、变体和空位填充:
title: "@lang(main:notice.title)"
在打开界面前绑定语言包即可。语言重载或切换后,LinView 会自动重绘当前插件已打开的 GUI,保留会话、状态、分页、动态区已有行及静态控件覆盖,不重新调用 Source。
Bukkit 中标题变化时会重新打开同尺寸容器,但保留 Core 会话与点击路由;标题不变时只更新内容。通过 GuiWidget 提供的普通字符串仍是固定值,不会自动变成语言引用。
lin.linView().reload() 会重新加载 GUI 定义并再次加载 Source,仅修改语言文件时不必调用它。界面配置请见资源文件 ⇱,变量规则请见占位符 ⇱。其他模块的取值与刷新方式见配置文本 ⇱。
讨论