公司的项目现在经常要对接国外客户,所以我们的官网需要有个支持多语言切换的功能。一谈起多语言,第一反应就是 i18n,那我们就开始吧!
我们官网是基于 Nuxt 开发的,所以需要引入 @nuxtjs/i18n
依赖。
我们从创建 Nuxt 项目开始。
创建 Nuxt 项目
1
| npx nuxi@latest init <project-name>
|
安装 i18n 依赖:
注意事项
我们用的是最新的Nuxt 3,感觉不太稳定。如果遇到某个包报以下错误:
1 2 3
| node_modules\wide-align\align.js to a dynamic import() which is available in all CommonJS modules.
Instead change the require of index.js in node_modules\wide-align\align.js to a dynamic import() which is available in all CommonJS modules.
|
说明在 CommonJS 模块中使用了 ES Module 的 import
语法而导致的,这里指向的是 string-width
这个包。解决方案是,在 package.json
里添加以下配置:
1 2 3 4 5
| "resolutions": {
"string-width": "4.2.3"
}
|
resolutions: 在项目中有多个包依赖于同一个模块,但这些包可能依赖于不同版本的该模块时,可能会遇到冲突或不兼容的问题。通过使用 resolutions
,可以强制所有包使用指定的版本,从而解决这些冲突。
在 Yarn 中使用:这项功能主要用于 Yarn,因此确保使用的是 Yarn 而不是 npm。如果使用的是 Yarn,则可以在运行 yarn install
时,它会遵循 resolutions
字段的配置。记得把 lock 文件删掉再重新 yarn
。
创建语言文件
接下来,我们创建 lang
文件夹,并放置对应的语言文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| // en.js
export default { nav: { home: "home", ems: "ems", cas: "cas", factory: "factory", about: "about", }, home: { test: "test" } };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| zh.js
export default { nav: { home: "首页", ems: "储能EMS", cas: "碳核算CAS", factory: "工厂数字化", about: "关于我们", }, home: { test: "测试" } };
|
创建 i18n 配置
创建 i18n.config.js
文件:
1 2 3
| export default defineI18nConfig(() => ({ legacy: false, }));
|
配置 nuxt.config.ts
1 2 3 4 5 6 7 8 9 10 11 12
| export default defineNuxtConfig({ modules: ['@nuxtjs/i18n'], i18n: { locale: 'zh', locales: [ { code: 'zh', name: '中文', file: 'zh.js' }, { code: 'en', name: 'English', file: 'en.js' }, ], vueI18n: './i18n.config.js', langDir: 'lang/', } });
|
创建中间件
我们还需要创建一个中间件,让页面跳转到 home,其实就是路由守卫。
创建 middleware
文件夹,文件名为 redirect.global.ts
:
1 2 3 4 5
| export default defineNuxtRouteMiddleware((to, from) => { if (to.fullPath === '/') { return navigateTo('/home'); } });
|
创建布局
在 app.vue
中加入:
1
| <NuxtLayout></NuxtLayout>
|
然后创建 layouts
文件夹,并在其中创建 default.vue
默认文件:
1 2 3 4 5
| <template> <PageHeader /> <NuxtPage></NuxtPage> </div> </template>
|
开发导航组件
创建 components
文件夹,并在其中创建 PageHeader.vue
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| <template> <div class="hidden lg:flex header-menu-container items-center justify-end"> <div class="px-4 w-[100px] py-2 bg-[#409eff] cursor-pointer text-center rounded-md text-white" @click="setLocale('zh')">中文</div> <div class="px-4 w-[100px] py-2 mx-6 bg-[#409eff] cursor-pointer text-center rounded-md text-white" @click="setLocale('en')">en</div> <div class="flex"> <div v-for="item in menus" :key="item.link" @mouseover="hoveredLink = item.link" @mouseleave="hoveredLink = null" class="box-border sm:text-lg md:text-xl lg:text-xl px-10 py-5 mx-5 text-[#0F2628] hover:text-black relative" > <NuxtLink :to="localePath(item.link)" @click="selectLink(item.link)">{{ $t(item.key) }}</NuxtLink> <div class="absolute left-0 right-0 bottom-0 h-[4px] bg-[#0F2628] transition-all duration-200 rounded" :class="{ 'opacity-100': isActive(item.link) || hoveredLink === item.link, 'opacity-0': !(isActive(item.link) || hoveredLink === item.link) }" ></div> </div> </div> </div> </template>
<script setup> import { ref, computed, watch } from 'vue'; import { useRoute } from 'vue-router'; const { locale, setLocale } = useI18n(); const localePath = useLocalePath();
const menus = ref([ { link: "/home", title: "首页", key: "nav.home" }, { link: "/ems", title: "储能EMS", key: "nav.ems" }, { link: "/cas", title: "碳核算CAS", key: "nav.cas" }, { link: "/factory", title: "工厂数字化", key: "nav.factory" }, { link: "/about", title: "关于我们", key: "nav.about" }, ]);
const hoveredLink = ref(null); const selectedLink = ref(null); const route = useRoute();
const selectLink = (link) => { selectedLink.value = localePath(link); };
const isActive = (link) => { return selectedLink.value === link || route.path === localePath(link); };
</script>
<style scoped> .header-menu-container { height: 140px; margin-right: 30px; } </style>
|
创建页面
在 pages
文件夹里创建 home
和 ems
文件:
1 2 3 4 5
| home.vue {{ $t("home.description") }}
ems.vue {{ $t("ems.description") }}
|
效果展示
ok,这样就搞定了。不但可以多语言切换,而且刷新浏览器的时候,也会记住对应menu状态和语言模式。
我的微信公众号: 梨的前端小屋