4

我有一个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 }

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

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

我怎样才能做到这一点,最好的方法是什么?

4

2 回答 2

9

你可以实现路由器

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

并设置localebeforeEach钩子上

// 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();

});
于 2018-05-14T01:06:16.993 回答
3

我可以想到将所有路由嵌套在单个/: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 个字符,并且脚本和区域是可选的。如果您使用规范化标签(小写主要、titlecase 脚本、大写区域),那就更好了,因为这将进一步减少歧义:

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

    您需要更仔细地阅读规范,而不是确保 regex 是正确的。您还需要防止beforeEach钩子中不受支持的语言环境,以便您可以加载“未找到”错误页面。

    只要您不定义其第一个路径段可能被误认为语言环境标记的任何路由,上述内容应该可以解决歧义问题。

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

    请注意,以 / 开头的嵌套路径将被视为根路径。这允许您利用组件嵌套,而无需使用嵌套 URL。

    错误地定义绝对路径将导致路由仅匹配回退(我假设是英语)区域设置。由于开发人员可能大部分时间都使用英语进行原型设计和测试,因此看起来似乎没有任何问题。

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

  3. 每次使用<RouterLink>和编程导航都需要注入语言环境参数。您需要记住插入$i18n.locale到每个to道具和push()调用中。不这样做不会导致错误或破坏页面,因此您的测试不太可能捕捉到这一点,如果您只用英文浏览,您不会注意到任何问题。<RouterLink>您可以使用自己的自动执行此操作的组件进行包装或扩展,但这并不能防止有人意外使用RouterLink,因为它仍然是全局注册的。你也可以编写一个全局 mixin 来为router.push()/ .replace()/添加方便的方法.go(),但这同样不能防止你意外使用这些方法。

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

于 2020-09-01T00:06:49.827 回答