Astro + @astrolicious/i18n 實作 i18n 多語系功能
之前有使用過 用 Paraglide 做 Astro i18n,但做起來就是很卡。後來發現 I18n for Astro (@astrolicious/i18n) 這個套件更好用,於是就改用這個套件來做多語系功能。
安裝 @astrolicious/i18n
@astrolicious/i18n 是基於 i18next 這個套件來做的,因此也需要安裝 i18next:
yarn add i18next @astrolicious/i18n然後註冊 i18n 套件:
import i18n from '@astrolicious/i18n'
export default defineConfig({ integrations: [ i18n({ defaultLocale: 'zh-TW', locales: ['zh-TW', 'en'], }), ],})在 Layout 中加入 I18nHead 元件來處理 SEO 相關的 meta:
---import I18nHead from '@astrolicious/i18n/components/I18nHead.astro'import { getHtmlAttrs, getLocale } from 'i18n:astro'
const locale = getLocale()
const htmlLangMap: Record<string, string> = { 'zh-TW': 'zh-Hant-TW',}
const htmlAttrs = getHtmlAttrs()htmlAttrs.lang = htmlLangMap[locale] || locale---
<!doctype html><html {...htmlAttrs}> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width" />
<I18nHead /> </head> <body> ... </body></html>設定多語系檔案
在 src/locales/ 目錄下建立語系檔案,檔案格式為 JSON:
{ "nav_about": "About", "nav_home": "Home", "nav_posts": "Posts"}{ "nav_about": "關於", "nav_home": "首頁", "nav_posts": "文章"}使用翻譯
然後在元件或頁面中使用 t 函式來取得翻譯:
---import { t, getHtmlAttrs, getLocale, getLocalePath } from 'i18n:astro'---
<!doctype html><html {...htmlAttrs}> <head> ... </head> <body> <ul class="menu"> <li class:list={['menu-item', pathname === getLocalePath('/') && 'active']}> <a href={getLocalePath('/')}>{t('nav_home')}</a> </li> <li class:list={['menu-item', pathname.startsWith(getLocalePath('/posts/')) && 'active']}> <a href={getLocalePath('/posts/')}>{t('nav_posts')}</a> </li> <li class:list={['menu-item', pathname === getLocalePath('/about/') && 'active']}> <a href={getLocalePath('/about/')}>{t('nav_about')}</a> </li> </ul> </body></html>新增語言選單
可以顯示語系文字後,就可以新增語言選單切換語系:
---import { t, getHtmlAttrs, getLocale, getLocalePath, getSwitcherData } from 'i18n:astro'
const switcherData = getSwitcherData()
const switcherLabels: Record<string, string> = { en: 'English', 'zh-TW': '繁體中文',}---
<!doctype html><html {...htmlAttrs}> <head> ... </head> <body> <ul class="menu"> ... <li class="menu-item menu-item-lang menu-item-has-children"> <a href="#"> <i class="fas fa-globe"></i> {switcherLabels[locale]} </a> <ul class="sub-menu"> {switcherData.map(item => ( <li class:list={['menu-item', item.locale === locale && 'active']}> <a href={`${item.href.replace(/\/$/, '')}/`}>{switcherLabels[item.locale]}</a> </li> ))} </ul> </li> </ul> </body></html>完整程式碼
以下是完整的 Layout 程式碼:
---import { t, getHtmlAttrs, getLocale, getLocalePath, getSwitcherData } from 'i18n:astro'
const locale = getLocale()const switcherData = getSwitcherData()
const switcherLabels: Record<string, string> = { en: 'English', 'zh-TW': '繁體中文',}
const htmlLangMap: Record<string, string> = { 'zh-TW': 'zh-Hant-TW',}
const htmlAttrs = getHtmlAttrs()htmlAttrs.lang = htmlLangMap[locale] || locale---
<!doctype html><html {...htmlAttrs}> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width" />
<I18nHead /> </head> <body> <ul class="menu"> <li class:list={['menu-item', pathname === getLocalePath('/') && 'active']}> <a href={getLocalePath('/')}>{t('nav_home')}</a> </li> <li class:list={['menu-item', pathname.startsWith(getLocalePath('/posts/')) && 'active']}> <a href={getLocalePath('/posts/')}>{t('nav_posts')}</a> </li> <li class:list={['menu-item', pathname === getLocalePath('/about/') && 'active']}> <a href={getLocalePath('/about/')}>{t('nav_about')}</a> </li> <li class="menu-item menu-item-lang menu-item-has-children"> <a href="#"> <i class="fas fa-globe"></i> {switcherLabels[locale]} </a> <ul class="sub-menu"> {switcherData.map(item => ( <li class:list={['menu-item', item.locale === locale && 'active']}> <a href={`${item.href.replace(/\/$/, '')}/`}>{switcherLabels[item.locale]}</a> </li> ))} </ul> </li> </ul> </body></html>- 作者:Lucas Yang
- 文章連結:https://star-note-lucas.me/posts/astro-i18n-astrolicious
- 版權聲明:本部落格所有文章除特別聲明外,均採用 CC BY-NC-SA 4.0 許可協議。轉載請註明出處。