You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring多语言消息变量替换异常:非英文环境变量未正确替换

非英文环境下错误消息变量未正确替换的解决方案

问题背景

英文环境(正常工作)

  • 消息配置文件 cds-messages.properties:
409003=''{0}'' is a required field (*).
  • 变量映射文件 error-messages-label-mapping.properties:
name=Program Name
type_id=Evaluation Type
  • UI最终显示正确:
'Program Name' is a required field (*).
'Evaluation Type' is a required field (*).

非英文环境(以德语为例,异常)

  • 德语消息配置 cds-messages_de.properties:
409003=„{0}“ ist ein Pflichtfeld (*).
  • 德语变量映射 error-messages-label-mapping_de.properties:
name=Programmname
type_id=Auswertungsart
  • UI实际输出(变量未替换):
„name“ ist ein Pflichtfeld (*).
„type_id“ ist ein Pflichtfeld (*).

当前Spring配置 application.yaml:

spring:
  application:
    name: evaluation-service
  messages:
    basename: "cds-messages,error-messages-label-mapping"

注:将德语消息中的„{0}“改为英文格式''{0}''后变量可正常替换,但无法修改翻译团队提供的文件。

问题根源

Spring默认的MessageFormat只识别英文单引号转义格式(''{0}''),对于其他语言的特殊引号(如德语的„和“),会把包裹的{0}当成普通文本,无法识别为占位符,因此无法完成变量替换。

可行解决方案

方案1:自定义MessageFormat适配特殊引号

在Spring消息处理的底层做适配,无需修改业务代码:

  1. 自定义MessageFormat子类,处理特殊引号的占位符识别:
import java.text.MessageFormat;
import java.text.ParsePosition;
import java.util.Locale;

public class LocalizedMessageFormat extends MessageFormat {
    private final String leftQuote;
    private final String rightQuote;

    public LocalizedMessageFormat(String pattern, Locale locale) {
        super(pattern, locale);
        // 根据语言设置对应的引号
        if (locale.getLanguage().equals("de")) {
            this.leftQuote = "„";
            this.rightQuote = "“";
        } else {
            // 默认英文引号
            this.leftQuote = "''";
            this.rightQuote = "''";
        }
        // 预处理消息模板,剥离引号让占位符被识别
        applyPattern(processPattern(pattern));
    }

    private String processPattern(String pattern) {
        // 移除引号包裹,让{0}被识别为占位符
        return pattern.replace(leftQuote + "(\\{\\d+\\})" + rightQuote, "$1");
    }

    @Override
    public String format(Object[] arguments, StringBuffer result, ParsePosition pos) {
        StringBuffer temp = new StringBuffer();
        super.format(arguments, temp, pos);
        // 重新添加对应语言的引号
        result.append(leftQuote).append(temp).append(rightQuote);
        return result.toString();
    }
}
  1. 自定义ReloadableResourceBundleMessageSource,使用上面的自定义格式类:
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import java.text.MessageFormat;
import java.util.Locale;

public class CustomMessageSource extends ReloadableResourceBundleMessageSource {
    @Override
    protected MessageFormat createMessageFormat(String msg, Locale locale) {
        return new LocalizedMessageFormat(msg, locale);
    }
}
  1. 配置Spring使用自定义的MessageSource:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MessageConfig {
    @Bean
    public CustomMessageSource messageSource() {
        CustomMessageSource source = new CustomMessageSource();
        source.setBasenames("cds-messages", "error-messages-label-mapping");
        source.setDefaultEncoding("UTF-8");
        return source;
    }
}

方案2:业务层手动处理变量替换

快速实现小范围适配,适合语言种类不多的场景:

  1. 编写消息格式化工具类:
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
public class ErrorMessageFormatter {
    private final MessageSource messageSource;
    // 匹配不同语言的引号+变量占位符,可扩展其他语言
    private static final Pattern DE_PATTERN = Pattern.compile("„(\\w+)“");

    public ErrorMessageFormatter(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    public String format(String errorCode, String variableKey, Locale locale) {
        // 获取原始错误消息模板
        String rawMsg = messageSource.getMessage(errorCode, null, locale);
        // 获取变量对应的本地化标签
        String label = messageSource.getMessage(variableKey, null, variableKey, locale);

        // 根据语言选择对应的替换规则
        if (locale.getLanguage().equals("de")) {
            Matcher matcher = DE_PATTERN.matcher(rawMsg);
            return matcher.replaceAll("„" + label + "“");
        } else {
            // 英文环境直接用默认格式化
            return messageSource.getMessage(errorCode, new Object[]{label}, locale);
        }
    }
}
  1. 业务代码中调用工具类:
// 示例:获取德语环境下name字段的错误消息
String errorMsg = errorMessageFormatter.format("409003", "name", Locale.GERMAN);

方案3:调整Spring消息转义配置(局限性大)

通过开启强制消息格式化,配合自定义转义字符,但仅适合简单场景:
修改MessageSource配置:

@Bean
public ReloadableResourceBundleMessageSource messageSource() {
    ReloadableResourceBundleMessageSource source = new ReloadableResourceBundleMessageSource();
    source.setBasenames("cds-messages", "error-messages-label-mapping");
    source.setDefaultEncoding("UTF-8");
    // 强制所有消息都经过MessageFormat处理
    source.setAlwaysUseMessageFormat(true);
    // 可以自定义转义字符,但无法适配多语言不同引号,灵活性差
    return source;
}

推荐方案

优先选择方案1,它在Spring底层完成多语言适配,对业务代码无侵入,也不需要修改翻译文件;如果项目语言种类少、需要快速上线,方案2的工具类方式更直接。

内容的提问来源于stack exchange,提问作者Sindhu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 23:53:09