Ember.js切换语言环境时如何重新渲染页面链接?
解决Ember多语言URL前缀与页面链接自动更新的问题
我之前也遇到过类似的场景,用ember-i18n做多语言同时要满足SEO的URL前缀需求,切换语言时页面链接不更新确实很头疼。下面给你一套可行的解决方案,分两种思路来处理:
思路1:用路由结构原生支持语言前缀(推荐)
最符合Ember路由机制的方式是把语言作为顶级路由参数,这样href-to会自动感知当前语言并生成正确的链接,不需要额外修改辅助函数。
1. 调整路由配置
把所有业务路由嵌套在语言路由下:
// app/router.js import EmberRouter from '@ember/routing/router'; import config from 'your-app/config/environment'; export default class Router extends EmberRouter { location = config.locationType; rootURL = config.rootURL; } Router.map(function() { // 语言前缀路由,:lang 作为参数 this.route('localized', { path: '/:lang' }, function() { // 这里放你所有的业务路由 this.route('posts', function() { this.route('show', { path: ':post_id' }); }); this.route('about'); }); });
2. 初始化语言与路由跳转
在应用路由的钩子中从URL获取语言参数,设置i18n的locale:
// app/routes/localized.js import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; export default class LocalizedRoute extends Route { @service i18n; beforeModel(transition) { // 从路由参数获取语言,默认用'en' const lang = transition.params.localized.lang || 'en'; this.i18n.set('locale', lang); } }
3. 语言切换逻辑
切换语言时,用transitionTo跳转到当前路由的对应语言版本,全程无页面刷新:
// app/controllers/application.js import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; export default class ApplicationController extends Controller { @service i18n; @service router; actions = { switchLanguage(newLang) { // 获取当前路由的名称和参数 const currentRoute = this.router.currentRouteName.replace('localized.', ''); const currentParams = this.router.currentRoute.params; // 更新locale并跳转到对应语言的当前路由 this.i18n.set('locale', newLang); this.router.transitionTo(`localized.${currentRoute}`, newLang, currentParams.post_id); } }; }
这种方式下,{{href-to 'posts'}}会自动生成/en/posts或/zh/posts这样的链接,语言切换后所有链接会自动更新,完全符合你的需求。
思路2:重写href-to辅助函数(适合不想改路由结构的场景)
如果不想调整现有路由结构,可以重写href-to,让它自动在生成的链接前加上当前语言前缀。
1. 重写href-to辅助函数
// app/helpers/href-to.js import HrefToHelper from 'ember-href-to/helpers/href-to'; import { inject as service } from '@ember/service'; export default class LocalizedHrefToHelper extends HrefToHelper { @service i18n; compute(params, hash) { // 调用原生href-to生成原始路径 const originalPath = super.compute(params, hash); const currentLang = this.i18n.locale; // 移除旧的语言前缀(防止重复添加) const langRegex = /^\/[a-z]{2}(-[a-z]{2})?\//; // 支持en、en-US这类格式 const pathWithoutLang = originalPath.replace(langRegex, '/'); // 拼接新的语言前缀 if (originalPath === '/') { return `/${currentLang}/`; } return `/${currentLang}${pathWithoutLang}`; } }
2. 语言切换时触发链接更新
切换语言后,需要让所有使用href-to的组件重新渲染。可以通过在i18n的locale变化时,发送一个全局事件,让相关组件重新计算:
// app/services/i18n.js(如果没有就新建) import I18n from 'ember-i18n/services/i18n'; import { inject as service } from '@ember/service'; export default class I18nService extends I18n { @service eventBus; // 需要自己实现一个简单的事件总线服务 set locale(newLocale) { super.locale = newLocale; // 发送locale变化事件 this.eventBus.trigger('localeChanged'); } }
然后在使用href-to的组件中监听这个事件,触发重新渲染:
// 比如在application组件中 import Component from '@ember/component'; import { inject as service } from '@ember/service'; import { scheduleOnce } from '@ember/runloop'; export default class ApplicationComponent extends Component { @service eventBus; init() { super.init(...arguments); this.eventBus.on('localeChanged', this, this.rerender); } willDestroy() { super.willDestroy(...arguments); this.eventBus.off('localeChanged', this, this.rerender); } }
这种方式不需要修改路由,但需要额外处理组件的重新渲染,相对来说不如第一种思路优雅。
关键注意事项
- 所有页面链接必须使用
{{href-to}}辅助函数,不能写静态的<a href="/posts">,否则无法自动更新。 - 配合FastBoot做服务端渲染,进一步提升SEO效果,确保搜索引擎能正确抓取不同语言版本的页面。
- 语言切换时绝对不要直接修改
window.location.pathname,会触发全页面刷新,破坏Ember的客户端路由优势。
内容的提问来源于stack exchange,提问作者G-we
相关产品推荐
相关产品推荐

