在很多官网项目中经常会遇到要做双语的需求,我总结了一些经验和大家一起学习交流
使用 Next.js
next.js 的服务端渲染 ssr、静态化 ssg 以及静态化增量渲染 isr 可以完美的将动态数据适配 seo,本身又是 react 技术栈开发方式 前端er都很熟悉,所以目前在大型官网项目中作为首选框架是很合适的
国际化在 next.js 中很容易实现,它自带国际化路由,比如 /detail 要跳转英文版就是 /en/detail,会在路由之前添加 /en语言路径。首先在配置文件 next.config.js 里设置 i18n 语言选项
这里就用 next.js v13 来做示例
// next.config.js
module.exports = {
// ...
i18n: {
locales: ['zh-CN', 'en'],
defaultLocale: 'zh-CN', // 默认语言
},
// ...
}
locales 中添加了语言列表(中/英),默认语言 defaultLocale 设为中文,默认语言的页面路由不需要添加语言路径前缀,比如中文的新闻页路由直接为 /news 就行,英文版就是 /en/news
我们通常需要在网站头部加一个语言切换的组件,让用户去手动点击切换。
创建一个 LanguageSwitcher 组件:
import React from 'react'
import { useRouter } from 'next/router'
import Link from 'next/link'
const LanguageSwitcher: React.FC = () => {
const { locales, locale, pathname, query, asPath } = useRouter()
return (
<div className="locale-switcher">
{locales?.map((item) => (
<Link
href={{ pathname, query }}
as={asPath}
locale={item}
key={item}
>
<div className="switcher-item">{item}</div>
</Link>
))}
</div>
)
}
通过 useRouter hook 可以拿到语言列表 locales、当前页面语言 locale,所有路由的属性 pathname, query, asPath,<Link /> 的 locale 属性可以传入将要访问的语言,这样用户点击跳转后 会自动在路由路径前加上语言路径
在页面同样可以通过 useRouter 的 locale 做判断进行条件渲染,这里只是简单示例:
// 页面
import { useRouter } from 'next/router'
import type { NextPage } from 'next'
import css from './page.module.scss'
const HomePage: NextPage = () => {
const { locale } = useRouter
return (
<div className={css.page_wrap}>
<h1>{locale === 'en' ? 'English page' : '中文页面'}</h1>
</div>
)
}
当然,大家肯定还有其他的方案,但我觉得国际化路由是最简易可行的
在 h5 普通静态网页中
普通静态网页实现国际化方案非常灵活,大家可自定义规则,一般做两个 html 比如一个中文的一个英文的 news.html news-en.html 有些需求要在不同语言展示不同布局的页面。我会在 html 文件名加 - 横杠语言后缀
同样,在网页头部也有个切换语言的地方:
<div class="language-switch">
<a data-language-item="zh_CN" href="javascript:">中文</a>
<a data-language-item="en" href="javascript:">English</a>
</div>
用自定义data属性 data-language-item 来设置语言值,方便在 js 中可以监听点击事件拿到将访问的语言,原理很简单 就是链接到对应语言的 html 页面
创建语言列表数组常量,第一项就为默认语言
// 语言列表
const LANGUAGE_LIST = [
'zh_CN', // 第一项为默认语言,默认语言的html名不带后缀
'en',
]
要点击跳转到对应语言页面,就要先知道当前页面的语言是什么,以及不带语言后缀的页面名称,用函数 getCurrentLanguage 来封装下
函数 getFileName 用正则获取浏览器 url 中的 html 名
但是 url 会出现两种都要考虑到的情况,比如 http://xx.com/news-en.html 和 http://xx.com/news-en,后者可能是 nginx 中设置的和 html 文件名一致的路由,配置代码很简单,我会在文末贴出来
// 获取文件名
function getFileName(filePath) {
return filePath.replace(/(.*\/)*([^.]+).*/ig,'$2')
}
// 从浏览器url中获取当前语言、是否为默认语言、没有语言后面的页面名 purePageName
function getCurrentLanguage() {
const pageName = getFileName(window.location.href)
let curLanguage = LANGUAGE_LIST[0]
for (let i = 0; i < LANGUAGE_LIST.length; i++) {
let _wArr = pageName.split('-')
if (_wArr[_wArr.length - 1] === LANGUAGE_LIST[i]) {
curLanguage = LANGUAGE_LIST[i]
break
}
}
const isDefaultLanguage = curLanguage === LANGUAGE_LIST[0]
const purePageName = isDefaultLanguage ? pageName : pageName.replace(`-${curLanguage}`, '')
return { curLanguage, isDefaultLanguage, purePageName }
}
函数 getCurrentLanguage 中先设置当前语言默认为数组 LANGUAGE_LIST 的第一项,然后遍历 LANGUAGE_LIST 通过截取 url 末尾的 - 拿到当前语言,默认语言的页面 url 是不带语言后缀的
调用 getCurrentLanguage() 能返回 curLanguage isDefaultLanguage purePageName,最终需要监听有 data-language-item 属性的 dom 节点的 click 事件跳转到相应的语言页面
const languageItemEls = document.querySelectorAll('[data-language-item]')
const { curLanguage, purePageName } = getCurrentLanguage()
Array.from(languageItemEls).forEach((el) => {
if (el.getAttribute('data-language-item') === curLanguage) {
el.classList.add('active')
} else {
el.classList.remove('active')
}
el.addEventListener(event.click, (e) => {
const originalUrl = window.location.href
const hasParams = originalUrl.indexOf('?') >= 0
// 如果url中有参数就存为后缀
//(?id=1&name=jack&backUrl=test.html)暂不支持参数中值为html地址还另外带参数的,只有微信公众号回跳地址会出现这种情况 用vue/react来做
const suffix = hasParams ? `?${originalUrl.split('?')[1]}` : ''
const pageUrl = hasParams ? originalUrl.split('?')[0] : originalUrl
const pageName = getFileName(pageUrl)
const targetLanguage = el.getAttribute('data-language-item') ?? LANGUAGE_LIST[0]
if (curLanguage !== targetLanguage) {
window.location.href = targetLanguage === LANGUAGE_LIST[0]
? pageUrl.replace(pageName, purePageName) + suffix
: pageUrl.replace(pageName, `${purePageName}-${targetLanguage}`) + suffix
}
})
})
Nginx 配置中设置路由url去掉.html后缀(/name.html -> /name):
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /$1.html last;
break;
}
}