初始化项目
This commit is contained in:
57
.gitignore
vendored
Normal file
57
.gitignore
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<<<<<<< HEAD
|
||||||
|
target/
|
||||||
|
out/
|
||||||
|
lib/
|
||||||
|
libs/
|
||||||
|
.vscode/
|
||||||
|
.codex/
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
*.db
|
||||||
|
*.sqlite
|
||||||
|
*.sqlite3
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
=======
|
||||||
|
# ---> YHPlugin
|
||||||
|
# Maven
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Build
|
||||||
|
out/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# AI
|
||||||
|
.codex/
|
||||||
|
|
||||||
|
# Local libraries
|
||||||
|
lib/
|
||||||
|
libs/
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
src/test/
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Database
|
||||||
|
*.db
|
||||||
|
*.sqlite
|
||||||
|
*.sqlite3
|
||||||
|
|
||||||
|
# Java
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
>>>>>>> 12c7b3f43c6113b170b9e83dedf75650d51b9040
|
||||||
459
README.md
Normal file
459
README.md
Normal file
@@ -0,0 +1,459 @@
|
|||||||
|
<<<<<<< HEAD
|
||||||
|
# 1.LangUtil 是什么?
|
||||||
|
|
||||||
|
LangUtil 是一个 Minecraft 1.12.2 Bukkit / Spigot 多语言管理插件。
|
||||||
|
|
||||||
|
它的作用不是替代业务插件,而是给其他插件提供统一的语言 API:
|
||||||
|
|
||||||
|
- 每个插件维护自己的语言文件
|
||||||
|
- 玩家可以切换自己的语言
|
||||||
|
- 不同玩家可以看到不同语言
|
||||||
|
- 修改语言文件后可以不重启服务器直接重载
|
||||||
|
- 支持物品名称、物品 lore、普通消息、占位符和颜色代码
|
||||||
|
|
||||||
|
简单理解:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
LangUtil = 公共语言管理器
|
||||||
|
其他插件 = 自己提供 lang 文件
|
||||||
|
玩家 = 自己选择 zh_CN / en_US 等语言
|
||||||
|
```
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
# 2.目录结构
|
||||||
|
|
||||||
|
## LangUtil 自己的数据
|
||||||
|
|
||||||
|
玩家语言选择会保存到:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
plugins/LangUtil/player-language.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
格式示例:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
player-language:
|
||||||
|
550e8400-e29b-41d4-a716-446655440000: zh_CN
|
||||||
|
```
|
||||||
|
|
||||||
|
## 其他插件的语言文件
|
||||||
|
|
||||||
|
其他插件必须把语言文件放在自己的插件目录下:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
plugins/OtherPlugin/lang/zh_CN.yml
|
||||||
|
plugins/OtherPlugin/lang/en_US.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
语言文件不是放在 LangUtil 的 lang 目录
|
||||||
|
语言文件是放在调用 register 的插件自己的 lang 目录
|
||||||
|
```
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
# 3.其他插件如何注册?
|
||||||
|
|
||||||
|
其他插件需要依赖 LangUtil。
|
||||||
|
|
||||||
|
## plugin.yml
|
||||||
|
|
||||||
|
```yml
|
||||||
|
depend:
|
||||||
|
- LangUtil
|
||||||
|
```
|
||||||
|
|
||||||
|
## 插件入口
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.io.yaohun.langutil.LangUtil;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
public final class OtherPlugin extends JavaPlugin {
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
LangUtil.register(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
当执行:
|
||||||
|
|
||||||
|
```java
|
||||||
|
LangUtil.register(this);
|
||||||
|
```
|
||||||
|
|
||||||
|
LangUtil 会读取:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
plugins/OtherPlugin/lang/*.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
# 4.语言文件怎么写?
|
||||||
|
|
||||||
|
## zh_CN.yml
|
||||||
|
|
||||||
|
```yml
|
||||||
|
message:
|
||||||
|
reload: "&a语言文件已重载"
|
||||||
|
no-permission: "&c你没有权限使用该命令。"
|
||||||
|
|
||||||
|
item:
|
||||||
|
flame-sword:
|
||||||
|
name: "&c烈焰长剑"
|
||||||
|
lore:
|
||||||
|
- "&7伤害: &c{damage}"
|
||||||
|
- "&7品质: &6{quality}"
|
||||||
|
- ""
|
||||||
|
- "&e右键释放火焰"
|
||||||
|
```
|
||||||
|
|
||||||
|
## en_US.yml
|
||||||
|
|
||||||
|
```yml
|
||||||
|
message:
|
||||||
|
reload: "&aLanguage files reloaded"
|
||||||
|
no-permission: "&cYou do not have permission."
|
||||||
|
|
||||||
|
item:
|
||||||
|
flame-sword:
|
||||||
|
name: "&cFlame Sword"
|
||||||
|
lore:
|
||||||
|
- "&7Damage: &c{damage}"
|
||||||
|
- "&7Quality: &6{quality}"
|
||||||
|
- ""
|
||||||
|
- "&eRight-click to release fire"
|
||||||
|
```
|
||||||
|
|
||||||
|
文件名就是语言 ID:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
zh_CN.yml → zh_CN
|
||||||
|
en_US.yml → en_US
|
||||||
|
```
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
# 5.玩家命令
|
||||||
|
|
||||||
|
## 查看可用语言
|
||||||
|
|
||||||
|
```yml
|
||||||
|
/lang
|
||||||
|
```
|
||||||
|
|
||||||
|
玩家会看到当前已经加载的语言 ID。
|
||||||
|
|
||||||
|
## 切换语言
|
||||||
|
|
||||||
|
```yml
|
||||||
|
/lang zh_CN
|
||||||
|
/lang en_US
|
||||||
|
```
|
||||||
|
|
||||||
|
切换后会按玩家 UUID 保存。
|
||||||
|
|
||||||
|
玩家退出服务器再进入,仍然保持上次选择。
|
||||||
|
|
||||||
|
## 重载语言文件
|
||||||
|
|
||||||
|
```yml
|
||||||
|
/lang reload
|
||||||
|
```
|
||||||
|
|
||||||
|
修改语言文件后,不需要重启服务器,执行该命令即可重新读取所有已注册插件的语言文件。
|
||||||
|
|
||||||
|
控制台也可以执行:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
lang reload
|
||||||
|
```
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
# 6.权限节点
|
||||||
|
|
||||||
|
```yml
|
||||||
|
langutil.use # 允许玩家使用 /lang 查看和切换语言
|
||||||
|
langutil.reload # 允许使用 /lang reload
|
||||||
|
```
|
||||||
|
|
||||||
|
推荐配置:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
普通玩家: langutil.use
|
||||||
|
管理员: langutil.use + langutil.reload
|
||||||
|
```
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
# 7.API 调用
|
||||||
|
|
||||||
|
## 获取普通消息
|
||||||
|
|
||||||
|
```java
|
||||||
|
String message = LangUtil.get(player, this, "message.reload");
|
||||||
|
```
|
||||||
|
|
||||||
|
## 发送普通消息
|
||||||
|
|
||||||
|
```java
|
||||||
|
LangUtil.send(player, this, "message.reload");
|
||||||
|
```
|
||||||
|
|
||||||
|
## 发送消息并播放音效
|
||||||
|
|
||||||
|
```java
|
||||||
|
LangUtil.send(player, this, "message.reload", Sound.LEVEL_UP);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用占位符
|
||||||
|
|
||||||
|
```java
|
||||||
|
Map<String, String> placeholders = new HashMap<String, String>();
|
||||||
|
placeholders.put("damage", "12");
|
||||||
|
placeholders.put("quality", "史诗");
|
||||||
|
|
||||||
|
String name = LangUtil.get(player, this, "item.flame-sword.name", placeholders);
|
||||||
|
```
|
||||||
|
|
||||||
|
语言文件:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
item:
|
||||||
|
flame-sword:
|
||||||
|
name: "&c烈焰长剑"
|
||||||
|
```
|
||||||
|
|
||||||
|
结果:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
烈焰长剑
|
||||||
|
```
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
# 8.物品 name 和 lore 怎么做?
|
||||||
|
|
||||||
|
物品 name 是字符串,可以用:
|
||||||
|
|
||||||
|
```java
|
||||||
|
LangUtil.get(player, this, "item.flame-sword.name", placeholders);
|
||||||
|
```
|
||||||
|
|
||||||
|
物品 lore 是 List,可以用:
|
||||||
|
|
||||||
|
```java
|
||||||
|
LangUtil.getList(player, this, "item.flame-sword.lore", placeholders);
|
||||||
|
```
|
||||||
|
|
||||||
|
完整示例:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Map<String, String> placeholders = new HashMap<String, String>();
|
||||||
|
placeholders.put("damage", "12");
|
||||||
|
placeholders.put("quality", "史诗");
|
||||||
|
|
||||||
|
ItemMeta meta = item.getItemMeta();
|
||||||
|
meta.setDisplayName(LangUtil.get(player, this, "item.flame-sword.name", placeholders));
|
||||||
|
meta.setLore(LangUtil.getList(player, this, "item.flame-sword.lore", placeholders));
|
||||||
|
item.setItemMeta(meta);
|
||||||
|
```
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
# 9.推荐封装方式
|
||||||
|
|
||||||
|
为了让业务代码更短,其他插件可以自己封装一个 `Lang` 工具类。
|
||||||
|
|
||||||
|
```java
|
||||||
|
public final class Lang {
|
||||||
|
private static JavaPlugin plugin;
|
||||||
|
|
||||||
|
private Lang() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void init(JavaPlugin javaPlugin) {
|
||||||
|
plugin = javaPlugin;
|
||||||
|
LangUtil.register(javaPlugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void send(CommandSender sender, String path) {
|
||||||
|
LangUtil.send(sender, plugin, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String get(CommandSender sender, String path) {
|
||||||
|
return LangUtil.get(sender, plugin, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getList(CommandSender sender, String path) {
|
||||||
|
return LangUtil.getList(sender, plugin, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
插件启动时:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Lang.init(this);
|
||||||
|
```
|
||||||
|
|
||||||
|
业务代码:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Lang.send(player, "message.reload");
|
||||||
|
```
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
# 10.重载机制说明
|
||||||
|
|
||||||
|
LangUtil 不会每次发送消息都读取本地 YAML。
|
||||||
|
|
||||||
|
语言文件读取时机:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
插件启动 register 时
|
||||||
|
/lang reload 时
|
||||||
|
再次调用 register 时
|
||||||
|
```
|
||||||
|
|
||||||
|
平时调用:
|
||||||
|
|
||||||
|
```java
|
||||||
|
LangUtil.get(...)
|
||||||
|
LangUtil.getList(...)
|
||||||
|
LangUtil.send(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
都是从内存缓存读取。
|
||||||
|
|
||||||
|
这样可以避免每次发消息都读磁盘,降低主线程压力。
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
# 11.异常与回退规则
|
||||||
|
|
||||||
|
## 玩家没有选择语言
|
||||||
|
|
||||||
|
默认使用:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
zh_CN
|
||||||
|
```
|
||||||
|
|
||||||
|
## 玩家选择的语言文件不存在
|
||||||
|
|
||||||
|
回退:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
zh_CN
|
||||||
|
```
|
||||||
|
|
||||||
|
## zh_CN 也不存在
|
||||||
|
|
||||||
|
返回 path 原文:
|
||||||
|
|
||||||
|
```java
|
||||||
|
LangUtil.get(player, this, "message.not-found");
|
||||||
|
```
|
||||||
|
|
||||||
|
返回:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
message.not-found
|
||||||
|
```
|
||||||
|
|
||||||
|
## lore 不存在
|
||||||
|
|
||||||
|
返回单行 List:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
- item.xxx.lore
|
||||||
|
```
|
||||||
|
|
||||||
|
## YAML 格式错误
|
||||||
|
|
||||||
|
控制台会输出:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
插件名
|
||||||
|
语言文件名
|
||||||
|
错误原因
|
||||||
|
```
|
||||||
|
|
||||||
|
不会因为单个语言文件错误导致服务器关闭。
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
# 12.实战使用流程
|
||||||
|
|
||||||
|
## 第一步:安装 LangUtil
|
||||||
|
|
||||||
|
把插件放入服务器:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
plugins/LangUtil-1.2.0.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
启动服务器。
|
||||||
|
|
||||||
|
## 第二步:业务插件依赖 LangUtil
|
||||||
|
|
||||||
|
```yml
|
||||||
|
depend:
|
||||||
|
- LangUtil
|
||||||
|
```
|
||||||
|
|
||||||
|
## 第三步:业务插件注册语言文件
|
||||||
|
|
||||||
|
```java
|
||||||
|
LangUtil.register(this);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 第四步:创建语言文件
|
||||||
|
|
||||||
|
```yml
|
||||||
|
plugins/OtherPlugin/lang/zh_CN.yml
|
||||||
|
plugins/OtherPlugin/lang/en_US.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## 第五步:玩家切换语言
|
||||||
|
|
||||||
|
```yml
|
||||||
|
/lang zh_CN
|
||||||
|
/lang en_US
|
||||||
|
```
|
||||||
|
|
||||||
|
## 第六步:业务插件发送消息
|
||||||
|
|
||||||
|
```java
|
||||||
|
LangUtil.send(player, this, "message.reload");
|
||||||
|
```
|
||||||
|
|
||||||
|
## 第七步:修改语言后重载
|
||||||
|
|
||||||
|
```yml
|
||||||
|
/lang reload
|
||||||
|
```
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
# 13.注意事项
|
||||||
|
|
||||||
|
- 推荐始终使用带 `JavaPlugin plugin` 参数的 API。
|
||||||
|
- 不推荐使用 `LangUtil.get(String path)`,因为它无法稳定判断读取哪个插件的语言。
|
||||||
|
- 其他插件必须先调用 `LangUtil.register(this)`,否则读取不到自己的语言文件。
|
||||||
|
- 语言文件必须放在调用注册方法的插件自己的 `lang` 目录。
|
||||||
|
- 物品 lore 必须用 YAML List 格式编写。
|
||||||
|
- 修改语言文件后必须执行 `/lang reload` 才会进入缓存。
|
||||||
|
=======
|
||||||
|
# LangUtil
|
||||||
|
|
||||||
|
>>>>>>> 12c7b3f43c6113b170b9e83dedf75650d51b9040
|
||||||
57
pom.xml
Normal file
57
pom.xml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>com.douhun</groupId>
|
||||||
|
<artifactId>LangUtil</artifactId>
|
||||||
|
<version>1.2.1</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>LangUtil</name>
|
||||||
|
<description>Minecraft 1.12.2 多插件语言管理插件</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>spigot-repo</id>
|
||||||
|
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>spigot-api</artifactId>
|
||||||
|
<version>1.12.2-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>LangUtil-${project.version}</finalName>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.8.1</version>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
129
src/main/java/com/io/yaohun/langutil/LangCommand.java
Normal file
129
src/main/java/com/io/yaohun/langutil/LangCommand.java
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package com.io.yaohun.langutil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.command.TabCompleter;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
final class LangCommand implements CommandExecutor, TabCompleter {
|
||||||
|
private static final String PERMISSION_USE = "langutil.use";
|
||||||
|
private static final String PERMISSION_RELOAD = "langutil.reload";
|
||||||
|
|
||||||
|
private final LanguageRegistry languageRegistry;
|
||||||
|
private final PlayerLanguageStore playerLanguageStore;
|
||||||
|
|
||||||
|
LangCommand(LanguageRegistry languageRegistry, PlayerLanguageStore playerLanguageStore) {
|
||||||
|
this.languageRegistry = languageRegistry;
|
||||||
|
this.playerLanguageStore = playerLanguageStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
if (args.length == 1 && "reload".equalsIgnoreCase(args[0])) {
|
||||||
|
return handleReload(sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length == 0) {
|
||||||
|
return handleList(sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length == 1) {
|
||||||
|
return handleSwitch(sender, args[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
LangUtil.sendMessage(sender, "&c用法:/lang [语言ID|reload]");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||||
|
if (args.length != 1) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> suggestions = new ArrayList<String>();
|
||||||
|
String prefix = args[0].toLowerCase();
|
||||||
|
if ("reload".startsWith(prefix) && sender.hasPermission(PERMISSION_RELOAD)) {
|
||||||
|
suggestions.add("reload");
|
||||||
|
}
|
||||||
|
if (sender.hasPermission(PERMISSION_USE)) {
|
||||||
|
for (String languageId : this.languageRegistry.getAvailableLanguageIds()) {
|
||||||
|
if (languageId.toLowerCase().startsWith(prefix)) {
|
||||||
|
suggestions.add(languageId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return suggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleList(CommandSender sender) {
|
||||||
|
if (!sender.hasPermission(PERMISSION_USE)) {
|
||||||
|
LangUtil.sendMessage(sender, "&c你没有权限使用该命令。");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> languageIds = this.languageRegistry.getAvailableLanguageIds();
|
||||||
|
if (languageIds.isEmpty()) {
|
||||||
|
LangUtil.sendMessage(sender, "&e当前没有已加载的语言文件。");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LangUtil.sendMessage(sender, "&a当前可用语言:&f" + join(languageIds));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleSwitch(CommandSender sender, String languageId) {
|
||||||
|
if (!(sender instanceof Player)) {
|
||||||
|
LangUtil.sendMessage(sender, "&c该命令仅玩家可用。");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!sender.hasPermission(PERMISSION_USE)) {
|
||||||
|
LangUtil.sendMessage(sender, "&c你没有权限使用该命令。");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!this.languageRegistry.isLanguageAvailable(languageId)) {
|
||||||
|
LangUtil.sendMessage(sender, "&c该语言不存在。");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player player = (Player) sender;
|
||||||
|
this.playerLanguageStore.setLanguage(player, languageId);
|
||||||
|
LangUtil.sendMessage(sender, "&a你的语言已切换为 " + languageId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleReload(CommandSender sender) {
|
||||||
|
if (!sender.hasPermission(PERMISSION_RELOAD)) {
|
||||||
|
LangUtil.sendMessage(sender, "&c你没有权限使用该命令。");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReloadSummary summary = this.languageRegistry.reloadAll();
|
||||||
|
if (summary.hasFailures()) {
|
||||||
|
LangUtil.sendMessage(sender, "&c语言文件重载完成,但存在失败项:");
|
||||||
|
for (LanguageLoadFailure failure : summary.getFailures()) {
|
||||||
|
LangUtil.sendMessage(sender, "&c- " + failure.getPluginName() + "/" + failure.getFileName()
|
||||||
|
+ ":" + failure.getReason());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LangUtil.sendMessage(sender, "&a语言文件已重载。已加载:" + join(summary.getLoadedMessages()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String join(List<String> values) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (int index = 0; index < values.size(); index++) {
|
||||||
|
if (index > 0) {
|
||||||
|
builder.append(", ");
|
||||||
|
}
|
||||||
|
builder.append(values.get(index));
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/main/java/com/io/yaohun/langutil/LangService.java
Normal file
37
src/main/java/com/io/yaohun/langutil/LangService.java
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package com.io.yaohun.langutil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
final class LangService {
|
||||||
|
private final LanguageRegistry languageRegistry;
|
||||||
|
private final PlayerLanguageStore playerLanguageStore;
|
||||||
|
|
||||||
|
LangService(LanguageRegistry languageRegistry, PlayerLanguageStore playerLanguageStore) {
|
||||||
|
this.languageRegistry = languageRegistry;
|
||||||
|
this.playerLanguageStore = playerLanguageStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
void register(JavaPlugin plugin) {
|
||||||
|
this.languageRegistry.register(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getDefault(String path) {
|
||||||
|
return MessageFormatter.color(this.languageRegistry.getDefaultMessage(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
String get(CommandSender sender, JavaPlugin plugin, String path, Map<String, String> placeholders) {
|
||||||
|
String languageId = this.playerLanguageStore.getLanguage(sender);
|
||||||
|
String rawMessage = this.languageRegistry.getMessage(plugin, languageId, path);
|
||||||
|
String replacedMessage = MessageFormatter.replace(rawMessage, placeholders);
|
||||||
|
return MessageFormatter.color(replacedMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> getList(CommandSender sender, JavaPlugin plugin, String path, Map<String, String> placeholders) {
|
||||||
|
String languageId = this.playerLanguageStore.getLanguage(sender);
|
||||||
|
List<String> rawMessages = this.languageRegistry.getMessageList(plugin, languageId, path);
|
||||||
|
return MessageFormatter.formatList(rawMessages, placeholders);
|
||||||
|
}
|
||||||
|
}
|
||||||
102
src/main/java/com/io/yaohun/langutil/LangUtil.java
Normal file
102
src/main/java/com/io/yaohun/langutil/LangUtil.java
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package com.io.yaohun.langutil;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.bukkit.Sound;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
public final class LangUtil {
|
||||||
|
private static volatile LangService service;
|
||||||
|
|
||||||
|
private LangUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initialize(LangService langService) {
|
||||||
|
service = langService;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shutdown() {
|
||||||
|
service = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void register(JavaPlugin plugin) {
|
||||||
|
LangService current = service;
|
||||||
|
if (current == null) {
|
||||||
|
throw new IllegalStateException("LangUtil 尚未加载,无法注册语言文件。");
|
||||||
|
}
|
||||||
|
current.register(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不推荐使用:该方法缺少调用方插件上下文,无法稳定判断应读取哪个插件的语言文件。
|
||||||
|
*/
|
||||||
|
public static String get(String path) {
|
||||||
|
LangService current = service;
|
||||||
|
return current == null ? fallbackPath(path) : current.getDefault(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String get(CommandSender sender, JavaPlugin plugin, String path) {
|
||||||
|
return get(sender, plugin, path, Collections.<String, String>emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String get(CommandSender sender, JavaPlugin plugin, String path, Map<String, String> placeholders) {
|
||||||
|
LangService current = service;
|
||||||
|
return current == null ? fallbackPath(path) : current.get(sender, plugin, path, placeholders);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getList(CommandSender sender, JavaPlugin plugin, String path) {
|
||||||
|
return getList(sender, plugin, path, Collections.<String, String>emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getList(CommandSender sender, JavaPlugin plugin, String path, Map<String, String> placeholders) {
|
||||||
|
LangService current = service;
|
||||||
|
return current == null ? Collections.singletonList(fallbackPath(path)) : current.getList(sender, plugin, path, placeholders);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendMessage(CommandSender sender, String message) {
|
||||||
|
if (sender == null || message == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sender.sendMessage(MessageFormatter.color(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendMessage(CommandSender sender, String message, Sound sound) {
|
||||||
|
sendMessage(sender, message);
|
||||||
|
playSound(sender, sound);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void send(CommandSender sender, JavaPlugin plugin, String path) {
|
||||||
|
send(sender, plugin, path, Collections.<String, String>emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void send(CommandSender sender, JavaPlugin plugin, String path, Sound sound) {
|
||||||
|
send(sender, plugin, path);
|
||||||
|
playSound(sender, sound);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void send(CommandSender sender, JavaPlugin plugin, String path, Map<String, String> placeholders) {
|
||||||
|
if (sender == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sender.sendMessage(get(sender, plugin, path, placeholders));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void send(CommandSender sender, JavaPlugin plugin, String path, Map<String, String> placeholders, Sound sound) {
|
||||||
|
send(sender, plugin, path, placeholders);
|
||||||
|
playSound(sender, sound);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void playSound(CommandSender sender, Sound sound) {
|
||||||
|
if (sender instanceof Player && sound != null) {
|
||||||
|
Player player = (Player) sender;
|
||||||
|
player.playSound(player.getLocation(), sound, 1.0F, 1.0F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String fallbackPath(String path) {
|
||||||
|
return path == null ? "" : path;
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/main/java/com/io/yaohun/langutil/LangUtilPlugin.java
Normal file
42
src/main/java/com/io/yaohun/langutil/LangUtilPlugin.java
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package com.io.yaohun.langutil;
|
||||||
|
|
||||||
|
import org.bukkit.command.PluginCommand;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
public final class LangUtilPlugin extends JavaPlugin {
|
||||||
|
private LanguageRegistry languageRegistry;
|
||||||
|
private PlayerLanguageStore playerLanguageStore;
|
||||||
|
private LangService langService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
this.playerLanguageStore = new PlayerLanguageStore(this);
|
||||||
|
this.playerLanguageStore.load();
|
||||||
|
|
||||||
|
this.languageRegistry = new LanguageRegistry(this);
|
||||||
|
this.langService = new LangService(this.languageRegistry, this.playerLanguageStore);
|
||||||
|
LangUtil.initialize(this.langService);
|
||||||
|
|
||||||
|
LangCommand langCommand = new LangCommand(this.languageRegistry, this.playerLanguageStore);
|
||||||
|
PluginCommand command = getCommand("lang");
|
||||||
|
if (command != null) {
|
||||||
|
command.setExecutor(langCommand);
|
||||||
|
command.setTabCompleter(langCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
LangUtil.register(this);
|
||||||
|
getLogger().info("LangUtil 已加载。");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
if (this.playerLanguageStore != null) {
|
||||||
|
this.playerLanguageStore.save();
|
||||||
|
}
|
||||||
|
if (this.languageRegistry != null) {
|
||||||
|
this.languageRegistry.clear();
|
||||||
|
}
|
||||||
|
LangUtil.shutdown();
|
||||||
|
getLogger().info("LangUtil 已卸载。");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.io.yaohun.langutil;
|
||||||
|
|
||||||
|
final class LanguageLoadFailure {
|
||||||
|
private final String pluginName;
|
||||||
|
private final String fileName;
|
||||||
|
private final String reason;
|
||||||
|
|
||||||
|
LanguageLoadFailure(String pluginName, String fileName, String reason) {
|
||||||
|
this.pluginName = pluginName;
|
||||||
|
this.fileName = fileName;
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getPluginName() {
|
||||||
|
return this.pluginName;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getFileName() {
|
||||||
|
return this.fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getReason() {
|
||||||
|
return this.reason;
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/main/java/com/io/yaohun/langutil/LanguageLoadResult.java
Normal file
28
src/main/java/com/io/yaohun/langutil/LanguageLoadResult.java
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package com.io.yaohun.langutil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
|
final class LanguageLoadResult {
|
||||||
|
private final Map<String, YamlConfiguration> languages;
|
||||||
|
private final List<LanguageLoadFailure> failures = new ArrayList<LanguageLoadFailure>();
|
||||||
|
|
||||||
|
LanguageLoadResult(Map<String, YamlConfiguration> languages) {
|
||||||
|
this.languages = languages;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, YamlConfiguration> getLanguages() {
|
||||||
|
return this.languages;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addFailure(LanguageLoadFailure failure) {
|
||||||
|
this.failures.add(failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<LanguageLoadFailure> getFailures() {
|
||||||
|
return Collections.unmodifiableList(this.failures);
|
||||||
|
}
|
||||||
|
}
|
||||||
70
src/main/java/com/io/yaohun/langutil/LanguageLoader.java
Normal file
70
src/main/java/com/io/yaohun/langutil/LanguageLoader.java
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package com.io.yaohun.langutil;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.bukkit.configuration.InvalidConfigurationException;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
final class LanguageLoader {
|
||||||
|
private final JavaPlugin owner;
|
||||||
|
|
||||||
|
LanguageLoader(JavaPlugin owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
LanguageLoadResult load(JavaPlugin plugin) {
|
||||||
|
Map<String, YamlConfiguration> languages = new LinkedHashMap<String, YamlConfiguration>();
|
||||||
|
LanguageLoadResult result = new LanguageLoadResult(languages);
|
||||||
|
File langFolder = new File(plugin.getDataFolder(), "lang");
|
||||||
|
|
||||||
|
if (!langFolder.exists() || !langFolder.isDirectory()) {
|
||||||
|
this.owner.getLogger().info("插件 " + plugin.getName() + " 未提供 lang 目录,已跳过语言加载。");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
File[] files = langFolder.listFiles(new FileFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File file) {
|
||||||
|
return file.isFile() && file.getName().toLowerCase().endsWith(".yml");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (files == null || files.length == 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Arrays.sort(files, new Comparator<File>() {
|
||||||
|
@Override
|
||||||
|
public int compare(File left, File right) {
|
||||||
|
return left.getName().compareToIgnoreCase(right.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (File file : files) {
|
||||||
|
String languageId = file.getName().substring(0, file.getName().length() - ".yml".length());
|
||||||
|
YamlConfiguration configuration = new YamlConfiguration();
|
||||||
|
try {
|
||||||
|
configuration.load(file);
|
||||||
|
languages.put(languageId, configuration);
|
||||||
|
} catch (IOException exception) {
|
||||||
|
recordFailure(result, plugin, file, exception);
|
||||||
|
} catch (InvalidConfigurationException exception) {
|
||||||
|
recordFailure(result, plugin, file, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recordFailure(LanguageLoadResult result, JavaPlugin plugin, File file, Exception exception) {
|
||||||
|
String reason = exception.getMessage() == null ? exception.getClass().getSimpleName() : exception.getMessage();
|
||||||
|
LanguageLoadFailure failure = new LanguageLoadFailure(plugin.getName(), file.getName(), reason);
|
||||||
|
result.addFailure(failure);
|
||||||
|
this.owner.getLogger().warning("语言文件加载失败,插件=" + failure.getPluginName()
|
||||||
|
+ ",文件=" + failure.getFileName() + ",原因=" + failure.getReason());
|
||||||
|
}
|
||||||
|
}
|
||||||
180
src/main/java/com/io/yaohun/langutil/LanguageRegistry.java
Normal file
180
src/main/java/com/io/yaohun/langutil/LanguageRegistry.java
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
package com.io.yaohun.langutil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
final class LanguageRegistry {
|
||||||
|
static final String DEFAULT_LANGUAGE = "zh_CN";
|
||||||
|
|
||||||
|
private final JavaPlugin owner;
|
||||||
|
private final LanguageLoader languageLoader;
|
||||||
|
private final Map<String, RegisteredLanguageBundle> bundles = new LinkedHashMap<String, RegisteredLanguageBundle>();
|
||||||
|
|
||||||
|
LanguageRegistry(JavaPlugin owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
this.languageLoader = new LanguageLoader(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void register(JavaPlugin plugin) {
|
||||||
|
if (plugin == null) {
|
||||||
|
throw new IllegalArgumentException("注册语言文件时 plugin 不能为空。");
|
||||||
|
}
|
||||||
|
LanguageLoadResult result = this.languageLoader.load(plugin);
|
||||||
|
this.bundles.put(plugin.getName(), new RegisteredLanguageBundle(plugin, result.getLanguages()));
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized ReloadSummary reloadAll() {
|
||||||
|
ReloadSummary summary = new ReloadSummary();
|
||||||
|
List<JavaPlugin> plugins = new ArrayList<JavaPlugin>();
|
||||||
|
for (RegisteredLanguageBundle bundle : this.bundles.values()) {
|
||||||
|
plugins.add(bundle.getPlugin());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (JavaPlugin plugin : plugins) {
|
||||||
|
LanguageLoadResult result = this.languageLoader.load(plugin);
|
||||||
|
this.bundles.put(plugin.getName(), new RegisteredLanguageBundle(plugin, result.getLanguages()));
|
||||||
|
summary.addLoaded(plugin.getName(), result.getLanguages().size());
|
||||||
|
summary.addFailures(result.getFailures());
|
||||||
|
}
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized String getMessage(JavaPlugin plugin, String languageId, String path) {
|
||||||
|
if (path == null || path.length() == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
RegisteredLanguageBundle bundle = findBundle(plugin);
|
||||||
|
if (bundle == null) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
String selectedMessage = bundle.getMessage(languageId, path);
|
||||||
|
if (selectedMessage != null) {
|
||||||
|
return selectedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
String defaultMessage = bundle.getMessage(DEFAULT_LANGUAGE, path);
|
||||||
|
return defaultMessage == null ? path : defaultMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized List<String> getMessageList(JavaPlugin plugin, String languageId, String path) {
|
||||||
|
if (path == null || path.length() == 0) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
RegisteredLanguageBundle bundle = findBundle(plugin);
|
||||||
|
if (bundle == null) {
|
||||||
|
return Collections.singletonList(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> selectedMessages = bundle.getMessageList(languageId, path);
|
||||||
|
if (selectedMessages != null) {
|
||||||
|
return selectedMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> defaultMessages = bundle.getMessageList(DEFAULT_LANGUAGE, path);
|
||||||
|
return defaultMessages == null ? Collections.singletonList(path) : defaultMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized String getDefaultMessage(String path) {
|
||||||
|
if (path == null || path.length() == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
RegisteredLanguageBundle ownerBundle = this.bundles.get(this.owner.getName());
|
||||||
|
if (ownerBundle != null) {
|
||||||
|
String message = ownerBundle.getMessage(DEFAULT_LANGUAGE, path);
|
||||||
|
if (message != null) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (RegisteredLanguageBundle bundle : this.bundles.values()) {
|
||||||
|
String message = bundle.getMessage(DEFAULT_LANGUAGE, path);
|
||||||
|
if (message != null) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized boolean isLanguageAvailable(String languageId) {
|
||||||
|
if (languageId == null || languageId.length() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (RegisteredLanguageBundle bundle : this.bundles.values()) {
|
||||||
|
if (bundle.hasLanguage(languageId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized List<String> getAvailableLanguageIds() {
|
||||||
|
Set<String> languageIds = new LinkedHashSet<String>();
|
||||||
|
for (RegisteredLanguageBundle bundle : this.bundles.values()) {
|
||||||
|
languageIds.addAll(bundle.getLanguageIds());
|
||||||
|
}
|
||||||
|
List<String> result = new ArrayList<String>(languageIds);
|
||||||
|
Collections.sort(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void clear() {
|
||||||
|
this.bundles.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private RegisteredLanguageBundle findBundle(JavaPlugin plugin) {
|
||||||
|
if (plugin != null) {
|
||||||
|
RegisteredLanguageBundle bundle = this.bundles.get(plugin.getName());
|
||||||
|
if (bundle != null) {
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.bundles.get(this.owner.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class RegisteredLanguageBundle {
|
||||||
|
private final JavaPlugin plugin;
|
||||||
|
private final Map<String, YamlConfiguration> languages;
|
||||||
|
|
||||||
|
private RegisteredLanguageBundle(JavaPlugin plugin, Map<String, YamlConfiguration> languages) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.languages = new LinkedHashMap<String, YamlConfiguration>(languages);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JavaPlugin getPlugin() {
|
||||||
|
return this.plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasLanguage(String languageId) {
|
||||||
|
return this.languages.containsKey(languageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> getLanguageIds() {
|
||||||
|
return this.languages.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMessage(String languageId, String path) {
|
||||||
|
YamlConfiguration configuration = this.languages.get(languageId);
|
||||||
|
return configuration == null ? null : configuration.getString(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getMessageList(String languageId, String path) {
|
||||||
|
YamlConfiguration configuration = this.languages.get(languageId);
|
||||||
|
if (configuration == null || !configuration.contains(path)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (configuration.isList(path)) {
|
||||||
|
return configuration.getStringList(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
String singleLine = configuration.getString(path);
|
||||||
|
return singleLine == null ? null : Collections.singletonList(singleLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/main/java/com/io/yaohun/langutil/MessageFormatter.java
Normal file
48
src/main/java/com/io/yaohun/langutil/MessageFormatter.java
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package com.io.yaohun.langutil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
|
||||||
|
final class MessageFormatter {
|
||||||
|
private MessageFormatter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static String replace(String message, Map<String, String> placeholders) {
|
||||||
|
if (message == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (placeholders == null || placeholders.isEmpty()) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
String result = message;
|
||||||
|
for (Map.Entry<String, String> entry : placeholders.entrySet()) {
|
||||||
|
if (entry.getKey() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String value = entry.getValue() == null ? "" : entry.getValue();
|
||||||
|
result = result.replace("{" + entry.getKey() + "}", value);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String color(String message) {
|
||||||
|
if (message == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return ChatColor.translateAlternateColorCodes('&', message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<String> formatList(List<String> messages, Map<String, String> placeholders) {
|
||||||
|
List<String> result = new ArrayList<String>();
|
||||||
|
if (messages == null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
for (String message : messages) {
|
||||||
|
result.add(color(replace(message, placeholders)));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package com.io.yaohun.langutil;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
final class PlayerLanguageStore {
|
||||||
|
private static final String ROOT_PATH = "player-language";
|
||||||
|
|
||||||
|
private final JavaPlugin plugin;
|
||||||
|
private final File file;
|
||||||
|
private final Map<UUID, String> languages = new LinkedHashMap<UUID, String>();
|
||||||
|
|
||||||
|
PlayerLanguageStore(JavaPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.file = new File(plugin.getDataFolder(), "player-language.yml");
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void load() {
|
||||||
|
this.languages.clear();
|
||||||
|
if (!this.file.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(this.file);
|
||||||
|
ConfigurationSection section = configuration.getConfigurationSection(ROOT_PATH);
|
||||||
|
if (section == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String key : section.getKeys(false)) {
|
||||||
|
try {
|
||||||
|
UUID uuid = UUID.fromString(key);
|
||||||
|
String languageId = section.getString(key);
|
||||||
|
if (languageId != null && languageId.length() > 0) {
|
||||||
|
this.languages.put(uuid, languageId);
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException exception) {
|
||||||
|
this.plugin.getLogger().warning("玩家语言数据包含无效 UUID:" + key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void save() {
|
||||||
|
if (!this.plugin.getDataFolder().exists() && !this.plugin.getDataFolder().mkdirs()) {
|
||||||
|
this.plugin.getLogger().warning("无法创建插件数据目录:" + this.plugin.getDataFolder().getAbsolutePath());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
YamlConfiguration configuration = new YamlConfiguration();
|
||||||
|
for (Map.Entry<UUID, String> entry : this.languages.entrySet()) {
|
||||||
|
configuration.set(ROOT_PATH + "." + entry.getKey().toString(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
configuration.save(this.file);
|
||||||
|
} catch (IOException exception) {
|
||||||
|
this.plugin.getLogger().warning("保存玩家语言数据失败:" + exception.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized String getLanguage(CommandSender sender) {
|
||||||
|
if (sender instanceof Player) {
|
||||||
|
Player player = (Player) sender;
|
||||||
|
String languageId = this.languages.get(player.getUniqueId());
|
||||||
|
return languageId == null ? LanguageRegistry.DEFAULT_LANGUAGE : languageId;
|
||||||
|
}
|
||||||
|
return LanguageRegistry.DEFAULT_LANGUAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void setLanguage(Player player, String languageId) {
|
||||||
|
this.languages.put(player.getUniqueId(), languageId);
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/main/java/com/io/yaohun/langutil/ReloadSummary.java
Normal file
30
src/main/java/com/io/yaohun/langutil/ReloadSummary.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package com.io.yaohun.langutil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
final class ReloadSummary {
|
||||||
|
private final List<String> loadedMessages = new ArrayList<String>();
|
||||||
|
private final List<LanguageLoadFailure> failures = new ArrayList<LanguageLoadFailure>();
|
||||||
|
|
||||||
|
void addLoaded(String pluginName, int languageCount) {
|
||||||
|
this.loadedMessages.add(pluginName + ": " + languageCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addFailures(List<LanguageLoadFailure> newFailures) {
|
||||||
|
this.failures.addAll(newFailures);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> getLoadedMessages() {
|
||||||
|
return Collections.unmodifiableList(this.loadedMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<LanguageLoadFailure> getFailures() {
|
||||||
|
return Collections.unmodifiableList(this.failures);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasFailures() {
|
||||||
|
return !this.failures.isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/main/resources/lang/zh_CN.yml
Normal file
14
src/main/resources/lang/zh_CN.yml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
message:
|
||||||
|
reload: "&a语言文件已重载"
|
||||||
|
no-permission: "&c你没有权限使用该命令。"
|
||||||
|
command:
|
||||||
|
lang:
|
||||||
|
changed: "&a你的语言已切换为 {lang}"
|
||||||
|
item:
|
||||||
|
flame-sword:
|
||||||
|
name: "&c烈焰长剑"
|
||||||
|
lore:
|
||||||
|
- "&7伤害: &c{damage}"
|
||||||
|
- "&7品质: &6{quality}"
|
||||||
|
- ""
|
||||||
|
- "&e右键释放火焰"
|
||||||
16
src/main/resources/plugin.yml
Normal file
16
src/main/resources/plugin.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
name: LangUtil
|
||||||
|
main: com.io.yaohun.langutil.LangUtilPlugin
|
||||||
|
version: ${project.version}
|
||||||
|
author: Douhun
|
||||||
|
description: Minecraft 1.12.2 多插件语言管理插件
|
||||||
|
commands:
|
||||||
|
lang:
|
||||||
|
description: 查看、切换或重载语言文件
|
||||||
|
usage: /lang [语言ID|reload]
|
||||||
|
permissions:
|
||||||
|
langutil.use:
|
||||||
|
description: 允许玩家查看和切换个人语言
|
||||||
|
default: true
|
||||||
|
langutil.reload:
|
||||||
|
description: 允许重载所有已注册插件的语言文件
|
||||||
|
default: op
|
||||||
Reference in New Issue
Block a user