使用Thymeleaf配置国际化页面(语言切换)

页面国际化(通常缩写i18n)是指将软件、网站或应用程序设计成能够轻松地适应不同的语言、文化和地区的需求,以便更广泛地面向全球用户群。

本文采用thymeleaf提供的消息表达式#{}实现注册页面的语言切换

原文登录页面(login.html)

复制代码
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>用户登录</title>
    <!-- 引入独立CSS文件 -->
    <link rel="stylesheet" th:href="@{/css/login.css}">
</head>
<body>
<div class="login-container">
    <div class="login-header">
        <h2>欢迎登录</h2>
        <p>请输入您的账号和密码</p>
    </div>

    <!-- 提示信息 -->
    <div class="msg" th:text="${msg}" th:if="${msg != null}"></div>

    <!-- 登录表单 -->
    <form action="/login" method="post">
        <div class="form-item">
            <label for="username" >用户名</label>
            <input type="text" id="username" name="username" required placeholder="请输入用户名">
        </div>
        <div class="form-item">
            <label for="password">密码</label>
            <input type="password" id="password" name="password" required placeholder="请输入密码">
        </div>
        <!-- 验证码输入框 -->
        <div class="form-item">
            <label for="captcha">验证码</label>
            <div class="captcha-container">
                <input type="text" id="captcha" name="captcha" class="captcha-input" required placeholder="请输入验证码">
                <!-- 验证码图片,点击刷新 -->
                <img src="/verication" alt="验证码" class="captcha-img" onclick="this.src='/verication?'+Math.random()">
            </div>
        </div>
        <button type="submit" class="login-btn">登录</button>
    </form>

    <div class="link-area">
        <a href="/register">还没有账号?立即注册</a>
    </div>
</div>

<!--<script>-->
<!--    // 验证码图片点击刷新(防止缓存)-->
<!--    document.querySelector('.captcha-img').addEventListener('click', function() {-->
<!--        this.src = '/verication?' + Math.random();-->
<!--    });-->
<!--</script>-->
</body>
</html>

对应的CSS文件

复制代码
/* 全局样式重置 */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: 'Inter', 'Microsoft YaHei', sans-serif;
}

/* 页面背景 */
body {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 20px;
}

/* 登录容器 */
.login-container {
    background: rgba(255, 255, 255, 0.95);
    border-radius: 20px;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
    padding: 45px;
    width: 100%;
    max-width: 450px;
    border: 1px solid rgba(255, 255, 255, 0.2);
}

/* 登录头部 */
.login-header {
    text-align: center;
    margin-bottom: 35px;
}

.login-header h2 {
    color: #2d3748;
    font-size: 32px;
    font-weight: 600;
    margin-bottom: 10px;
}

.login-header p {
    color: #718096;
    font-size: 15px;
}

/* 提示信息 */
.msg {
    text-align: center;
    color: #e53e3e;
    font-size: 14px;
    padding: 10px;
    background: #fef2f2;
    border-radius: 8px;
    margin-bottom: 20px;
}

/* 表单项 */
.form-item {
    margin-bottom: 25px;
}

.form-item label {
    display: block;
    color: #4a5568;
    font-size: 14px;
    margin-bottom: 8px;
    font-weight: 500;
}

.form-item input {
    width: 100%;
    padding: 14px 18px;
    border: 1px solid #e2e8f0;
    border-radius: 12px;
    font-size: 15px;
    transition: all 0.3s ease;
    background: #f8fafc;
}

.form-item input:focus {
    outline: none;
    border-color: #667eea;
    box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
    background: #ffffff;
}

/* 验证码容器 */
.captcha-container {
    display: flex;
    gap: 10px;
}

.captcha-input {
    flex: 1;
}

.captcha-img {
    width: 120px;
    height: 48px;
    border-radius: 12px;
    cursor: pointer;
    border: 1px solid #e2e8f0;
}

/* 登录按钮 */
.login-btn {
    width: 100%;
    padding: 14px;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    border: none;
    border-radius: 12px;
    font-size: 16px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.3s ease;
    margin-bottom: 20px;
}

.login-btn:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 15px rgba(102, 126, 234, 0.3);
}

.login-btn:active {
    transform: translateY(0);
}

/* 链接区域 */
.link-area {
    text-align: center;
}

.link-area a {
    color: #667eea;
    text-decoration: none;
    font-size: 14px;
    font-weight: 500;
    transition: color 0.3s ease;
}

.link-area a:hover {
    color: #764ba2;
    text-decoration: underline;
}

一、修改 login.html 使用消息表达式

将页面中的硬编码文本替换为 Thymeleaf 的 #{...} 消息表达式,并添加语言切换链接。

复制代码
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title th:text="#{login.title}">用户登录</title>
    <link rel="stylesheet" th:href="@{/css/login.css}">
</head>
<body>
<div class="login-container">
    <div class="login-header">
        <h2 th:text="#{login.welcome}">欢迎登录</h2>
        <p th:text="#{login.subtitle}">请输入您的账号和密码</p>
    </div>

    <!-- 提示信息(后端传递的 msg 已国际化) -->
    <div class="msg" th:text="${msg}" th:if="${msg != null}"></div>

    <!-- 登录表单 -->
    <form th:action="@{/login}" method="post">
        <div class="form-item">
            <label for="username" th:text="#{login.username}">用户名</label>
            <input type="text" id="username" name="username" required th:placeholder="#{login.username.placeholder}">
        </div>
        <div class="form-item">
            <label for="password" th:text="#{login.password}">密码</label>
            <input type="password" id="password" name="password" required th:placeholder="#{login.password.placeholder}">
        </div>
        <!-- 验证码输入框 -->
        <div class="form-item">
            <label for="captcha" th:text="#{login.captcha}">验证码</label>
            <div class="captcha-container">
                <input type="text" id="captcha" name="captcha" class="captcha-input" required th:placeholder="#{login.captcha.placeholder}">
                <img src="/verication" alt="验证码" class="captcha-img" onclick="this.src='/verication?'+Math.random()">
            </div>
        </div>
        <button type="submit" class="login-btn" th:text="#{login.button}">登录</button>
    </form>

    <div class="link-area">
        <a th:href="@{/register}" th:text="#{login.register.link}">还没有账号?立即注册</a>
    </div>
    <br><br>
    <div class="link-area">
        <p class="mt-5 mb-3 text-muted" th:text="#{footer.copyright}">© 2026-2027</p>
        <br>
        <!-- 语言切换链接 -->
        <a class="link-area" th:href="@{/login(lang='zh_CN')}" th:text="#{lang.zh}">中文</a>&nbsp;&nbsp;&nbsp;
        <a class="link-area" th:href="@{/login(lang='en_US')}" th:text="#{lang.en}">English</a>
    </div>
</div>

<!--<script>-->
<!--    document.querySelector('.captcha-img').addEventListener('click', function() {-->
<!--        this.src = '/verication?' + Math.random();-->
<!--    });-->
<!--</script>-->
</body>
</html>

修改说明

  • 根据login.html文件,将所有中文的文本均用 #{key} 替换,并保留了原文本作为默认值(当资源文件找不到时显示)。

  • 表单的 action 改为 th:action="@{/login}",以支持相对路径和上下文路径。

  • 语言切换链接通过 th:href="@{/login(lang='zh_CN')}" 传递 lang 参数,点击后将改变当前页面的语言。

  • 添加了新的消息键,如 login.titlelogin.welcome 等。

二、创建国际化资源文件

src/main/resources 目录下创建i18n文件夹:

并在src/main/resources /i18n/目录下创建login.properties、login_en_Us.properties、login_zh_CN.properties三个配置文件,如上图所示

注意:SpringBoot默认识别的语言配置文件为src/main/resources /目录下的messages.properties,但是我们这里在src/main/resources /i18n/目录下创建login.properties、login_en_Us.properties、login_zh_CN.properties三个配置文件,所以后面需要在全局配置文件application.yaml中指定如下:

复制代码
#国际化文件基础名的配置(指定位置,默认resource/i18n/目录下编写login.properties)
messages:
  basename: i18n.login  //login是文件前缀

其他国际化的文件的名称必须严格按照 "文件的前缀名_语言代码_国家代码.properties"的形式命名,而且文件名"语言代码_国家代码"中间部分必须与前端代码中语言切换的@{/login(lang='zh_CN')参数一致。

复制代码
<!-- 语言切换链接 -->
        <a class="link-area" th:href="@{/login(lang='zh_CN')}" th:text="#{lang.zh}">中文</a>&nbsp;&nbsp;&nbsp;
        <a class="link-area" th:href="@{/login(lang='en_US')}" th:text="#{lang.en}">English</a>

login.properties文件设置如下:(按照实际login.html中的对应的中文文本设计的)

复制代码
# 登录页面
login.title=用户登录
login.welcome=欢迎登录
login.subtitle=请输入您的账号和密码
login.username=用户名
login.username.placeholder=请输入用户名
login.password=密码
login.password.placeholder=请输入密码
login.captcha=验证码
login.captcha.placeholder=请输入验证码
login.button=登录
login.register.link=还没有账号?立即注册

# 页脚
footer.copyright=© 2026-2027

# 语言名称
lang.zh=中文
lang.en=English

login_en_Us.properties文件设置如下:

复制代码
# 登录页面
login.title=User Login
login.welcome=Welcome Back
login.subtitle=Please enter your account and password
login.username=Username
login.username.placeholder=Enter username
login.password=Password
login.password.placeholder=Enter password
login.captcha=Captcha
login.captcha.placeholder=Enter captcha
login.button=Login
login.register.link=No account? Register now

# 页脚
footer.copyright=© 2026-2027

# 语言名称
lang.zh=中文
lang.en=English

login_zh_CN.properties文件设置如下:

复制代码
# 登录页面
login.title=用户登录
login.welcome=欢迎登录
login.subtitle=请输入您的账号和密码
login.username=用户名
login.username.placeholder=请输入用户名
login.password=密码
login.password.placeholder=请输入密码
login.captcha=验证码
login.captcha.placeholder=请输入验证码
login.button=登录
login.register.link=还没有账号?立即注册

# 页脚
footer.copyright=© 2026-2027

# 语言名称
lang.zh=中文
lang.en=English

三.在全局配置文件中指定国际化文件的信息

在application.yaml文件(更改国际化文件的默认指定位置,将指定位置改为默认resource/i18n/目录下编写login.properties)设置如下:

复制代码
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/message_user?useUnicode=true&characterEncoding=utf-8&useSSL=false
    driver-class-name: com.mysql.jdbc.Driver
  thymeleaf:
    cache: false   #是否启用thymeleaf模版缓存,在开发环境禁用
    encoding: utf-8
    mode: HTML5   #thymeleaf模版使用html模式(默认值:Html5)
    prefix: classpath:/templates/       #指定thymeleaf模版文件的位置(默认值:classpath:/tempaltes/)
    suffix: .html   #模版文件的后缀配置(默认.html)

  #国际化文件基础名的配置(指定位置,默认resource下的编写login.properties)
  messages:
    basename: i18n.login


mybatis:
  type-aliases-package: com.example.messagesystem.pojo     #实体类的全限定类的位置
  mapper-locations: classpath:mappers/*.xml                #mapper类的xml配置文件的位置

四、配置 LocaleChangeInterceptor

Spring 提供了 LocaleChangeInterceptor 来根据请求参数切换语言环境。在配置类中注册该拦截器,并设置参数名(例如 lang)。

创建一个配置类MyMvcConfig(如果有这个类,则直接在类中添加如下方法)

代码如下:

复制代码
package com.example.messagesystem.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

import java.util.Locale;

@Configuration   //配置类相当于xml配置文件
public class MyMvcConfig implements WebMvcConfigurer {
    //通过实现WebMvcConfigurer接口,可以自定义Spring MVC的配置(如拦截器、视图控制器、资源映射等)
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //用于注册视图控制器。视图控制器允许直接将请求URL与视图名称绑定,跳转过程不经过任何业务逻辑处理。
        //当用户访问应用根路径(如http://localhost:8080/)时,Spring MVC会自动将请求转发到名为index的视图。
        registry.addViewController("/").setViewName("login");  //登录
//        registry.addViewController("/login.html").setViewName("login");  //登录
//        registry.addViewController("/register.html").setViewName("register");  //注册
        registry.addViewController("/index.html").setViewName("index");//首页
    }


    //国际化页面设置
    // 1. 定义LocaleResolver,使用SessionLocaleResolver保存用户选择
    @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver resolver = new SessionLocaleResolver();
        resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE); // 默认中文
        return resolver;
    }

    // 2. 定义LocaleChangeInterceptor,拦截lang参数
    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
        interceptor.setParamName("lang"); // 参数名为lang
        return interceptor;
    }

    // 3. 注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }
}

说明:上述代码中

  • SessionLocaleResolver 将语言选择存储在用户的会话中,因此切换后整个会话都会保持该语言。

  • 默认语言设置为中文(Locale.SIMPLIFIED_CHINESE)。

  • 拦截器会检查请求参数 lang,并自动更改 LocaleResolver 中的语言环境。

五.检测前端login.html文件的切换设置

代码如下:

复制代码
<div class="link-area">
    <p class="mt-5 mb-3 text-muted" th:text="#{footer.copyright}">© 2026-2027</p>
    <br>
    <!-- 语言切换链接 -->
    <a class="link-area" th:href="@{/login(lang='zh_CN')}" th:text="#{lang.zh}">中文</a>&nbsp;&nbsp;&nbsp;
    <a class="link-area" th:href="@{/login(lang='en_US')}" th:text="#{lang.en}">English</a>
</div>

其中th:href="@{/login(lang='zh_CN')}"前端传lang参数值"zh_CN",必须与国际化配置文件名的中间"zh_CN"一致(例如login_en_Us.properties、login_zh_CN.properties两个国际化配置文件)

六.完整测试

  1. 启动应用,访问 http://localhost:8080/,页面默认显示中文。

  2. 点击底部的 English ,页面 URL 变为 http://localhost:8080/login?lang=en,所有文本切换为英文。

  3. 再次点击 中文,切换回中文。

  4. 刷新页面或重新打开浏览器,语言仍然保持上次选择(因为存储在 session 中)。

相关推荐
代码老中医1 小时前
接手老项目的一个月,我重构了那个2000行的“祖传”React组件
前端
用户83040713057012 小时前
路由传参刷新丢失问题:三种解决方案与最佳实践
前端
从文处安2 小时前
「前端何去何从」高效提示词(prompts):前端开发者的AI协作指南
前端·aigc
大时光2 小时前
gsap--《pink老师vivo官网实现》
前端
www_stdio2 小时前
全栈项目第五天:构建现代企业级 React 应用:从工程化到移动端实战的全链路指南
前端·react.js·typescript
my_styles2 小时前
window系统安装/配置Nginx
服务器·前端·spring boot·nginx
洋洋技术笔记2 小时前
Spring Boot自动装配原理
java·spring boot
神奇的程序员2 小时前
不止高刷:明基 RD280UG 在编码场景下的表现如何
前端