Skip to content

多语言系统

概述

多语言系统支持游戏界面的国际化,允许玩家根据自己的语言偏好查看翻译后的文本。系统支持中文、英文等多种语言,并提供占位符替换功能。

核心类

LanguageManager(语言管理器)

路径cn.enderrealm.disaster.lang.LanguageManager

语言管理器负责加载语言文件、获取翻译文本和处理占位符替换。

java
public class LanguageManager {
    private static final String DEFAULT_LANGUAGE = "en_us";
    private final Map<String, YamlConfiguration> languageFiles = new HashMap<>();
    private final File dataFolder;
    private String defaultLanguage = DEFAULT_LANGUAGE;
    
    public LanguageManager(File dataFolder, String configDefaultLanguage) {
        this.dataFolder = dataFolder;
        if (configDefaultLanguage != null && !configDefaultLanguage.isEmpty()) {
            this.defaultLanguage = configDefaultLanguage;
        }
        loadLanguages();
    }
}

语言加载

java
private void loadLanguages() {
    File langFolder = new File(dataFolder, "lang");
    if (!langFolder.exists()) {
        langFolder.mkdirs();
        return;
    }
    
    // 加载所有语言文件
    File[] files = langFolder.listFiles((dir, name) -> name.endsWith(".yml"));
    if (files != null) {
        for (File file : files) {
            String langCode = file.getName().replace(".yml", "");
            YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
            languageFiles.put(langCode.toLowerCase(), config);
        }
    }
}

文本获取

java
public String getText(String key, Player player, Object... args) {
    // 1. 获取玩家语言
    String clientLang = player != null ? player.getLocale().toLowerCase() : defaultLanguage;
    
    // 2. 尝试获取完整语言代码对应的配置
    YamlConfiguration langConfig = languageFiles.get(clientLang);
    
    // 3. 如果找不到,尝试使用语言的主要部分
    if (langConfig == null && clientLang.contains("_")) {
        String mainLang = clientLang.split("_")[0];
        for (String availableLang : languageFiles.keySet()) {
            if (availableLang.startsWith(mainLang)) {
                langConfig = languageFiles.get(availableLang);
                break;
            }
        }
    }
    
    // 4. 如果仍然没有找到,使用配置的默认语言
    if (langConfig == null) {
        langConfig = languageFiles.get(defaultLanguage.toLowerCase());
    }
    
    // 5. 如果配置的默认语言不存在,使用硬编码的默认语言
    if (langConfig == null) {
        langConfig = languageFiles.get(DEFAULT_LANGUAGE.toLowerCase());
    }
    
    // 6. 获取翻译文本
    if (langConfig != null) {
        String text = langConfig.getString(key);
        if (text != null) {
            // 7. 替换占位符
            if (args != null && args.length > 0) {
                for (int i = 0; i < args.length; i++) {
                    text = text.replace("{" + i + "}", args[i] != null ? args[i].toString() : "");
                }
            }
            return text;
        }
    }
    
    // 8. 如果所有尝试都失败了,返回 key 本身
    return key;
}

文本列表获取

java
public List<String> getTextList(String key, Player player) {
    // 类似 getText 的逻辑,但返回列表
    // ...
    
    if (langConfig != null) {
        List<String> textList = langConfig.getStringList(key);
        if (textList != null && !textList.isEmpty()) {
            return textList;
        }
    }
    
    return new ArrayList<>();
}

语言文件结构

文件位置

plugins/disaster/lang/
├── en_us.yml    # 英语(美国)
├── zh_cn.yml    # 中文(简体)
└── zh_tw.yml    # 中文(繁体)

文件格式

yaml
# en_us.yml
# 通用消息
no-permission: "§cYou don't have permission to use this command."
already-in-room: "§cYou are already in a room."
not-in-room: "§cYou are not in a room."
room-created: "§aRoom created successfully."
room-create-failed: "§cFailed to create room."
left-room: "§aYou have left the room."

# 房间消息
player-join-message: "§a{0} joined the room ({1}/{2})"
player-leave-message: "§c{0} left the room ({1}/{2})"
countdown-subtitle: "§eGame starts in {0} seconds"
countdown-cancelled: "§cCountdown cancelled - not enough players"
game-started: "§aGame started!"

# 游戏消息
game-start-title: "§6Disaster"
game-map-subtitle: "§7Map: {0}"
game-time-remaining: "§aTime: {0}"
game-over-title: "§cGame Over"
player-victory-subtitle: "§aYou survived!"
player-defeat-subtitle: "§cYou were eliminated"

# 灾难消息
disaster-countdown: "§cDisaster in {0}s"
disaster-zombie_apocalypse: "§4Zombie Apocalypse"
disaster-zombie_apocalypse-desc: "Zombies spawn around players every 30 seconds"
disaster-meteor_shower: "§6Meteor Shower"
disaster-meteor_shower-desc: "Fireballs rain from the sky every 10 seconds"
disaster-lightning: "§eLightning Storm"
disaster-lightning-desc: "Lightning strikes near players every 20 seconds"
# ... 更多灾难

# 记分板
scoreboard-title: "§6§lDisaster"
scoreboard-items:
  - "§7{0}"
  - "§fTime: §a{1}"
  - "§fSurvivors: §e{2}"
  - "§fDisaster: §c{3}"
scoreboard-no-disasters: "§7None"

# 物品
quit-item-name: "§cQuit Room"
mysterious-potion-name: "§dMysterious Potion"
mysterious-potion-lore: "§7Use to get random effects"
yaml
# zh_cn.yml
# 通用消息
no-permission: "§c你没有权限使用此命令。"
already-in-room: "§c你已经在房间中。"
not-in-room: "§c你不在房间中。"
room-created: "§a房间创建成功。"
room-create-failed: "§c房间创建失败。"
left-room: "§a你已离开房间。"

# 房间消息
player-join-message: "§a{0} 加入了房间 ({1}/{2})"
player-leave-message: "§c{0} 离开了房间 ({1}/{2})"
countdown-subtitle: "§e游戏将在 {0} 秒后开始"
countdown-cancelled: "§c倒计时取消 - 玩家数量不足"
game-started: "§a游戏开始!"

# 游戏消息
game-start-title: "§6灾难生存"
game-map-subtitle: "§7地图: {0}"
game-time-remaining: "§a时间: {0}"
game-over-title: "§c游戏结束"
player-victory-subtitle: "§a你存活了下来!"
player-defeat-subtitle: "§c你已被淘汰"

# 灾难消息
disaster-countdown: "§c灾难将在 {0} 秒后爆发"
disaster-zombie_apocalypse: "§4僵尸启示录"
disaster-zombie_apocalypse-desc: "每 30 秒在玩家周围生成僵尸"
disaster-meteor_shower: "§6流星雨"
disaster-meteor_shower-desc: "每 10 秒从天空降下火球"
disaster-lightning: "§e闪电风暴"
disaster-lightning-desc: "每 20 秒在玩家附近劈下闪电"
# ... 更多灾难

# 记分板
scoreboard-title: "§6§l灾难生存"
scoreboard-items:
  - "§7{0}"
  - "§f时间: §a{1}"
  - "§f存活: §e{2}"
  - "§f灾难: §c{3}"
scoreboard-no-disasters: "§7无"

# 物品
quit-item-name: "§c退出房间"
mysterious-potion-name: "§d神秘药水"
mysterious-potion-lore: "§7使用后随机获得效果"

占位符系统

占位符格式

占位符使用 {数字} 格式,从 0 开始:

yaml
player-join-message: "§a{0} 加入了房间 ({1}/{2})"

使用示例

java
// 获取翻译文本并替换占位符
String message = plugin.getLanguageManager().getText("player-join-message", player,
        player.getName(),           // {0}
        String.valueOf(players.size()),  // {1}
        String.valueOf(maxPlayers)       // {2}
);
player.sendMessage(message);

常用占位符

占位符描述示例
{0}玩家名称Steve
{1}当前玩家数3
{2}最大玩家数8
{3}灾难名称僵尸启示录
{4}时间02:30
{5}地图名称Map_01

语言选择机制

选择流程

  1. 获取玩家语言

    java
    String clientLang = player != null ? player.getLocale().toLowerCase() : defaultLanguage;
  2. 尝试完整语言代码

    java
    YamlConfiguration langConfig = languageFiles.get(clientLang);
  3. 尝试语言主要部分

    java
    if (langConfig == null && clientLang.contains("_")) {
        String mainLang = clientLang.split("_")[0];
        for (String availableLang : languageFiles.keySet()) {
            if (availableLang.startsWith(mainLang)) {
                langConfig = languageFiles.get(availableLang);
                break;
            }
        }
    }
  4. 使用配置的默认语言

    java
    if (langConfig == null) {
        langConfig = languageFiles.get(defaultLanguage.toLowerCase());
    }
  5. 使用硬编码的默认语言

    java
    if (langConfig == null) {
        langConfig = languageFiles.get(DEFAULT_LANGUAGE.toLowerCase());
    }

语言映射示例

玩家语言匹配结果
zh_cnzh_cn.yml
zh_twzh_tw.yml
zhzh_cn.yml(第一个匹配的 zh 开头语言)
en_usen_us.yml
en_gben_us.yml(第一个匹配的 en 开头语言)
fr_fr使用默认语言

配置示例

yaml
# 插件配置
Language: "zh_cn"  # 默认语言

相关文档