Servlet 国际化

Locale 检测、ResourceBundle 资源包、格式化数字与日期。

Intermediate
Key Points按 Accept-Language 选 Locale,从对应语言的 properties 文件取文案;代码里不出现任何硬编码文字。

三件事

AreaMechanism
TextResourceBundle + messages_xx.properties
NumbersNumberFormat 按 Locale 加千分位
DatesDateTimeFormatter.ofLocalizedDate

资源包

Text
src/main/resources/
├── messages.properties          # 默认(英文)
├── messages_zh.properties       # 中文
└── messages_ja.properties       # 日文
PROPERTIES
# messages.properties
greeting=Hello, {0}!
items=You have {0} items in the cart.
PROPERTIES
# messages_zh.properties(注意:properties 默认按 ISO-8859-1 读,中文要转义或用 UTF-8 读取)
greeting=\u4f60\u597d\uff0c{0}\uff01
items=\u8d2d\u7269\u8f66\u91cc\u6709 {0} \u4ef6\u5546\u54c1\u3002

在 Servlet 里用

Java
@WebServlet("/greet")
public class GreetServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        Locale locale = req.getLocale();          // 解析 Accept-Language 得到最匹配的
        ResourceBundle rb = ResourceBundle.getBundle("messages", locale);

        String name = req.getParameter("name");
        if (name == null) name = "Guest";

        String text = MessageFormat.format(rb.getString("greeting"), name);

        resp.setContentType("text/plain;charset=UTF-8");
        resp.getWriter().println(text);
    }
}
Output
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
→ 你好,Guest!

Accept-Language: en-US,en;q=0.9
→ Hello, Guest!

getLocale() returns the best match computed from the Accept-Language q-weights; getLocales() gives the full preference list for multi-level fallback.

中文 properties 的坑

getBundle decodes .properties as ISO-8859-1, so raw Chinese garbles. Either escape as \uXXXX (as above) or wrap a UTF-8 Reader in a PropertyResourceBundle.

Java
try (Reader r = Files.newBufferedReader(
        Path.of("messages_zh.properties"), StandardCharsets.UTF_8)) {
    ResourceBundle rb = new PropertyResourceBundle(r);
    System.out.println(rb.getString("greeting"));
}

格式化数字与日期

Java
Locale zh = Locale.SIMPLIFIED_CHINESE;
Locale us = Locale.US;

System.out.println(NumberFormat.getNumberInstance(zh).format(1234567.891));
System.out.println(NumberFormat.getNumberInstance(us).format(1234567.891));
System.out.println(NumberFormat.getCurrencyInstance(zh).format(1234.5));
Output
1,234,567.891
1,234,567.891
¥1,234.50
Internationalisation is more than translating strings: number formats, date order, currency symbols, plural rules and even colour and icon connotations all change. Extracting strings to properties is only about a third of the job.

Friends & Partners