在vue.js中使用语言环境添加前缀路由(使用vue-i18n)

时间:2018-05-13 23:33:28

标签: javascript vue.js internationalization vue-i18n

我有一个locale.js文件,负责定义用户区域设置。这是:

import store from '@/vuex/index'

let locale

const defaultLocale = 'en_US'

if (store.getters['auth/authenticated']) {
  locale = store.getters['auth/currentUser'].locale || defaultLocale
} else {
  if (localStorage.getItem('locale')) {
    locale = localStorage.getItem('locale')
  } else {
    locale = defaultLocale
  }
}

export default locale

此外,我还有一个i18n.js文件,负责制作我在启动应用时使用的i18n实例。

import Vue from 'vue'
import VueI18n from 'vue-i18n'
import locale from '@/services/locale'

Vue.use(VueI18n)

const fallbackLocale = 'en_US'

let i18n = new VueI18n({
  locale,
  fallbackLocale,
})

i18n.setLocaleMessage('ru_RU', require('@/lang/ru_RU.json'))
i18n.setLocaleMessage('en_US', require('@/lang/en_US.json'))

export { i18n }

现在我认为让网址前缀为/en/profile/ru/profile更为方便。这样我就可以与已经设置的语言环境共享一个链接。

不知道怎么做到这一点。将所有路由设为子路由并放置/:locale?并不方便,因为路由器尚未初始化(我在启动根应用实例时同时传递i18nrouter个实例。)

我如何实现这一目标,最佳方法是什么?

2 个答案:

答案 0 :(得分:2)

您可以实施路由器

routes: [{
    path: '/:lang',
    children: [
      {
        path: 'home'
        component: Home
      },
      {
        path: 'about',
        component: About
      },
      {
        path: 'contactus',
        component: ContactUs
      }
    ]
  }]

并在locale hook

中设置beforeEach
// use beforeEach route guard to set the language
router.beforeEach((to, from, next) => {

  // use the language from the routing param or default language
  let language = to.params.lang;
  if (!language) {
    language = 'en';
  }

  // set the current language for vuex-i18n. note that translation data
  // for the language might need to be loaded first
  Vue.i18n.set(language);
  next();

});

答案 1 :(得分:2)

我能想到的两个或三个问题是将所有路线嵌套在一个/:locale?下。

  1. 路由定义可能会变得模棱两可。如果您将路径/:locale?/foo/bar/:locale?/bar定义为路由,<RouterLink to="/foo/bar" />会匹配什么?这将取决于首先定义了哪些路由,如果我的第二个示例匹配,则会导致无效的语言环境。这个问题有一个简单的解决方案。只需使用正则表达式约束:locale参数即可。如果您静态地知道支持的语言环境的确切列表,则可以执行以下操作:

     import locales from '@/lang'       // Your list of supported locales.
    
     const regexp = locales.join('|')   // You may want to filter out 'en' first.
     const routes = [{
       path: `/:locale(${regexp})?`,
       children: [
         ...
       ],
     }]
    

    如果翻译和支持的语言环境列表仅在运行时可用(例如,通过API检索),则可能会被迫创建特定于您的语言环境标签格式的正则表达式。如果它们与BCP-47匹配,我相信这意味着主要子标签为2个或3个字符,并且脚本和区域是可选的。如果您使用标准化标签(小写的主标签,标题大写的脚本,大写的区域),那会更好,因为这将进一步减少歧义:

     const routes = [{
       path: '/:locale([a-z]{2,3}(-[A-Z][a-z]+)?(-([A-Z]{2}|[0-9]{3}))?',
       caseSensitive: true,
       children: [
         ...
       ],
     }]
    

    您将需要比我更仔细地阅读the spec,以确保正则表达式正确无误。您还需要防止在beforeEach钩中使用不受支持的语言环境,以便您可以加载“未找到”错误页面。

    只要您没有定义任何可能将第一个路径段误认为是语言环境标签的路由,上面的方法就可以解决歧义问题。

  2. 可能会意外地使用根路径定义路由。嵌套路由通常是使用相对路径定义的,即未使用/锚定的路径。但是,嵌套不仅仅是在许多路由之间共享前缀或参数的机制,它最常用于共享布局组件。因此,Vue-router允许您通过定义绝对路径来覆盖父路由定义的路径。 The documentation解释:

    请注意,以/开头的嵌套路径将被视为根路径。这样,您就可以利用组件嵌套,而不必使用嵌套的URL。

    错误地定义绝对路径将导致仅针对后备(我假设为英语)语言环境匹配该路由。由于开发人员大多数时候可能会使用英语进行原型设计和测试,因此看起来似乎不对劲。

    对于所有路径都在一个文件中定义的小型应用程序,这可能没什么大不了的,因为错误很容易发现。但是对于具有许多路由定义文件和许多开发人员的大型应用程序,这样的错误将更加难以捕获。

  3. 每次使用<RouterLink>和程序化导航都需要注入locale参数。您需要记住将$i18n.locale插值到每个to prop和push()调用中。不这样做不会导致错误或中断页面,因此您的测试不太可能捕获此错误,并且如果您仅使用英语浏览,则不会发现任何问题。您可以使用自己的组件来包装或扩展<RouterLink>,该组件会自动执行此操作,但这不会阻止某人意外使用RouterLink,因为它仍是全局注册的。您还可以编写全局混合来为router.push() / .replace() / .go()添加便捷方法,但这又不能防止您意外使用这些方法。

上述问题的一个不理想的解决方案是放弃将语言环境定义为路径参数,而是在初始化路由器之前将其匹配。为此,您必须将其作为base constructor option传递。不幸的是,base路径似乎不可更改,这意味着更改语言环境将需要一个新的页面请求。由于大多数用户最多可能会更改一次语言环境,因此这可能不是一个大问题,但仍不能提供最佳的用户体验。