由於重點和優先級的轉移,我們無法再維護此軟件包。它將不會收到更新,錯誤修復或新功能,並且隨著時間的推移可能變得不兼容。我們建議切換到支持最新next.js應用程序目錄的軟件包。
next-multilingual是Next.js應用程序的一種自以為是的端到端解決方案✱需要多種語言。
查看我們的演示應用程序!
sext next-multilingual僅與之搭配? pages目錄。我們仍在熨燙我們的解決方案以支持新的? app目錄由於國際化不再是Next.js的配置的一部分。
npm install next-multilingual
useMessages掛鉤,支持ICU MessageFormat和JSX注入開箱即用。/en-us/contact-us for Us English和/fr-ca/nous-joindre for Canadian French) 。 ✱您的默認語言環境sl必須與pages目錄文件系統匹配(例如,“關於我們”的slug應該在about-us目錄中)。如果您需要的默認場所使用文件系統支持的字符,則該字符尚未進行測試,並且可能無法正常工作。歡迎拉動請求嗎?
next-multilingual在將TSDOC添加到所有API中付出了很多努力。如果您不確定如何使用我們示例中提供的某些API,請直接在IDE中檢查。
另外,對“最佳實踐”有意見並不是一件容易的事。這就是為什麼我們在可以在此處諮詢的特殊文檔中記錄了設計決策的原因。如果您覺得我們的某些API不提供您期望的內容,請確保在打開問題之前諮詢此文檔。
對於那些希望直接跳入該動作的人,請在example目錄中查看next-multilingual實現的端到端實現。其餘部分,下面的部分將提供完整的,逐步的配置指南。
Next.js中有很多選擇可以實現我們的目標。 next-multilingual主要關心:
我們提供兩個API來簡化此步驟:
getConfig (簡單配置)此函數將生成sext.js配置,該配置將符合大多數用例。 getConfig採用以下參數:
applicationId將用作消息密鑰前綴的唯一應用程序標識符。locales - 您應用程序的環境。defaultLocale應用程序的默認場所(也必須包含在locales中)❗僅接受遵循
language的語言標籤 -country格式。有關原因的更多詳細信息,請參閱“設計決策文件”。
options (可選) - next.js配置對象的選項。 getConfig將返回next.js配置對象。
要使用它,只需在應用程序的next.config.js中添加以下代碼:
const { getConfig } = require ( 'next-multilingual/config' )
const config = getConfig ( 'exampleApp' , [ 'en-US' , 'fr-CA' ] , 'en-US' , {
// Put your optional options below.
poweredByHeader : false ,
} )
module . exports = config getConfig並不支持所有配置選項。如果您碰巧使用一個,錯誤消息將直接指向下一節:高級配置。
Config (高級配置)如果您有更多的高級需求,則可以直接使用Config對象,並直接在現有的next.config.js中插入next-multilingual所需的配置。 Config的參數幾乎與getConfig (減去options ) - 在您的IDE(TSDOC)中查看有關詳細信息。這是如何使用它的示例:
const { Config , webpackConfigurationHandler } = require ( 'next-multilingual/config' )
const config = new Config ( 'exampleApp' , [ 'en-US' , 'fr-CA' ] , 'en-US' )
module . exports = {
reactStrictMode : true ,
i18n : {
locales : config . getUrlLocalePrefixes ( ) ,
defaultLocale : config . getDefaultUrlLocalePrefix ( ) ,
localeDetection : false ,
} ,
poweredByHeader : false ,
webpack : webpackConfigurationHandler ,
}如果您需要自定義自己的webpack配置,我們建議這樣做這樣的處理程序:
import Webpack from 'webpack'
import { webpackConfigurationHandler , WebpackContext } from 'next-multilingual/config'
export const myWebpackConfigurationHandler = (
config : Webpack . Configuration ,
context : WebpackContext
) : Webpack . Configuration => {
const myConfig = webpackConfigurationHandler ( config , context )
// Do stuff here.
return myConfig
}或直接在next.config.js中:
// Webpack handler wrapping next-multilingual's handler.
const webpack = ( config , context ) => {
config = webpackConfigurationHandler ( config , context )
// Do stuff here.
return config
} next-multilingual/config在Next.js的當前路由功能上執行2件事:
next-multilingual/config還處理使用next-multilingual/link/ssr用於Link組件的局部URL所需的特殊WebPack配置,用於鏈接組件中的鏈接組件和next-multilingual/head/ssr ,用於Head組件中的規範和替代鏈接。
有關實現的更多詳細信息,例如為什麼我們使用UTF-8字符,請參閱設計決策文檔。
next-multilingual/messages/babel-plugin要顯示使用useMessages()掛鉤的本地化消息,我們需要配置自定義的Babel插件,該插件將自動將字符串注入頁面和組件。建議這樣做的方法是在您的應用程序底部包括一個.babelrc :
{
"presets" : [ " next/babel " ],
"plugins" : [ " next-multilingual/messages/babel-plugin " ]
}如果您不配置插件,則在嘗試使用useMessages時會遇到錯誤。
App ( _app.tsx )我們需要通過在pages目錄中添加_app.tsx來創建一個自定義App :
import { useActualLocale } from 'next-multilingual'
import type { AppProps } from 'next/app'
const ExampleApp : React . FC < AppProps > = ( { Component , pageProps } ) => {
useActualLocale ( ) // Forces Next.js to use the actual (proper) locale.
return < Component { ... pageProps } / >
}
export default ExampleApp這基本上做了兩件事,如評論中提到的那樣:
/ )的主頁上登錄主頁時將其重複使用。如果您不想使用next-multilingual環境檢測,則可以使用useActualLocale(false) 。Document ( _document.tsx )我們還需要通過在pages目錄中添加_document.tsx來創建自定義Document :
import { getHtmlLang } from 'next-multilingual'
import { DocumentProps , Head , Html , Main , NextScript } from 'next/document'
const Document : React . FC < DocumentProps > = ( documentProps ) => {
return (
< Html lang = { getHtmlLang ( documentProps ) } translate = "no" className = "notranslate" >
< Head >
< meta name = "google" content = "notranslate" / >
< / Head >
< body >
< Main / >
< NextScript / >
< / body >
< / Html >
)
}
export default Document這僅提供1個目的:在<html>標籤中顯示正確的服務器端語言環境。由於我們使用的是“假”默認場所,因此保持正確的SSR標記很重要,尤其是在解決/在 /上解決動態語言環境時。
next-multilingual/head提供<Head>組件,該組件會在標題中自動創建一個規範的鏈接和替代鏈接。這是Next.js開箱即用的東西。
NEXT_PUBLIC_ORIGIN環境變量根據Google,替代鏈接必須完全合格,包括傳輸方法(HTTP/HTTPS)。因為Next.js不知道在構建時間使用哪個URL,因此我們需要在環境變量中指定將使用的絕對URL。例如,對於開發環境,使用以下變量創建一個.env.development開發文件(根據您的設置進行調整):
NEXT_PUBLIC_ORIGIN =http://localhost:3000不管環境如何, next-multilingual將尋找一個稱為NEXT_PUBLIC_ORIGIN的變量來生成完全合格的URL。如果您使用的是next.js的basePath ,它將自動添加到基本URL中。
NEXT_PUBLIC_ORIGIN僅接受完全合格的域(例如, http://example.com ://example.com),沒有任何路徑。
next-multilingual ?現在已經配置了所有內容,我們可以專注於使用next-multilingual !
為了使next-multilingual按設計工作,我們必須找到解決兩個問題的解決方案:
undefined值:因為Next.js支持沒有地區的網站,因此其本地類型允許具有undefined值,而對於我們的情況來說,這更令人討厭,需要額外的鑄造。next-multilingual必須創建一個我們從未使用過的默認場所。這意味著要訪問相關的環境信息,我們不能依靠Next.js的API。我們創建了以下API,以允許在您的應用程序中允許一致的環境值:
useRouter這是Next.js's useRouter上的一個簡單包裝器,兩者都提供了正確的位置,但也永遠不會返回undefined 。
import { NextPage } from 'next'
import { useRouter } from 'next-multilingual/router'
const Page : NextPage = ( ) => {
const router = useRouter ( )
return < > { router . locale } < / >
}
export default Page getStaticPropsLocales import { getStaticPropsLocales } from 'next-multilingual'
export const getStaticProps : GetStaticProps = async ( context ) => {
const { locale , locales , defaultLocale } = getStaticPropsLocales ( context )
// do stuff
return { props : { } }
} getStaticPathsLocales import { getStaticPathsLocales } from 'next-multilingual'
export const getStaticPaths : GetStaticProps = async ( context ) => {
const { locales , defaultLocale } = getStaticPathsLocales ( context )
// do stuff
return { props : { } }
} getServerSidePropsLocales import { getServerSidePropsLocales } from 'next-multilingual'
export const getServerSideProps : GetServerSideProps = async ( context ) => {
const { locale , locales , defaultLocale } = getServerSidePropsLocales ( context )
// do stuff
return { props : { } }
}
配x 請注意,儘管我們建議使用智能語言環境檢測來動態渲染主頁,但這是完全可選的。通過將高級配置與localeDetection: true,您將不需要使用getServerSideProps還原Next.js行為。
主頁比其他頁面要復雜一些,因為我們需要出於以下原因實現動態場所檢測(和顯示):
/可能會對SEO產生負面影響,並且不是最佳的用戶體驗。next-multilingual帶有一個getPreferredLocale API,它提供的自動檢測功能比Next.js實現更明智。您可以在示例中找到完整的實現,但這是一個被剝離的版本:
import type { GetServerSideProps , NextPage } from 'next'
import { ResolvedLocaleServerSideProps , resolveLocale , useResolvedLocale } from 'next-multilingual'
import { useRouter } from 'next-multilingual/router'
const Homepage : NextPage < ResolvedLocaleServerSideProps > = ( { resolvedLocale } ) => {
// Force Next.js to use a locale that was resolved dynamically on the homepage (this must be the first action on the homepage).
useResolvedLocale ( resolvedLocale )
const { locale } = useRouter ( )
return < h1 > { locale } </ h1 >
}
export default Homepage
export const getServerSideProps : GetServerSideProps < ResolvedLocaleServerSideProps > = async (
context
) => {
return {
props : {
resolvedLocale : resolveLocale ( context ) ,
} ,
}
}簡而言之,這就是正在發生的事情:
next-multilingual環境餅乾中使用以前使用的語言環境。useResolvedLocale在路由器上覆蓋該值,以在整個應用程序上進行這種動態。每當您創建一個tsx , ts , jsx或js (可編譯)文件並需要本地化消息時,您就可以在受支持的Locales中創建一個消息文件,而這些文件只能由這些文件可用。就像CSS模塊一樣,您的想法是,您可以擁有與另一個文件的本地範圍關聯的消息文件。這具有使消息更模塊化的好處,並避免在不同上下文中共享消息(設計決策中的更多詳細信息有關為什麼這是不好的)。
消息文件有2個主要用例:
pages目錄中的頁面,您可以使用slug鍵標識符指定本地化的URL段( /末端或路徑末端的URL的一部分)。有關如何執行此操作的更多詳細信息。useMessages Hook提供。想像CSS,但對於可本質的字符串。總結:
創建和管理這些文件就像創建樣式表一樣簡單,但以下是重要的細節:
.properties文件。是的,您可能會想知道為什麼,但是設計決策文件中有充分的理由。UTF-8 。不這樣做會用�代替非拉丁字符 �.properties文件的一些內置IDE支持,我們遵循嚴格的命名約定: <PageFilename>.<locale>.properties<applicationId>.<context>.<id> where:next-multilingual/config中設置的相同值aboutUsPage或footerComponent上下文可能是上下文的好示例。每個文件只能包含1個上下文,並且不應在許多文件上使用上下文,因為這可能會導致“密鑰碰撞”(非唯一的密鑰)。.並且只能包含1至50個字母數字字符 - 我們建議使用駱駝盒以進行可讀性。slug標識符的密鑰的消息文件。title標識符中包含一個鍵。next-multilingual/messages中提供的getTitle API自動在title和slug密鑰之間退縮。useMessages掛鉤時才需要文件。另外,請確保檢查您的控制台日誌是否有關您的消息潛在問題的警告。習慣它首先工作可能很棘手,但是我們試圖使檢測和解決問題易於檢測。請注意,這些日誌只會顯示在非生產環境中。
在無法使用鉤子的同時需要本地化消息並不少見。一個例子是使用Next.js的核心功能之一是其內置API支持。在這種情況下,我們可以在指定locale參數時簡單地使用getMessages而不是使用useMessage 。
如前所述,有一個pages的特殊鍵,其中id是slug 。與看起來像this-is-a-page傳統sl,我們要求您以正常和人類可讀性的句子寫sl,以便可以像其他任何字符串一樣翻譯。這避免了對sl的特殊過程,這些過程可以用多種語言進行昂貴且複雜的管理。
基本上, slug是您頁面的人類可讀的“簡短描述”,代表URL的一個段( /或路徑末端之間的部分)。當用作URL段時,將應用以下轉換:
-例如, About Us將變成about-us 。
對於主頁,URL將始終為/這意味著不會使用slug鍵來創建局部URL段。
別忘了,必須將slug寫為正常的簡短描述,這意味著要跳過單詞以使SEO的短詞不建議使用。這樣做的主要原因是,如果您寫“一堆關鍵字”,那麼不熟悉SEO的語言學家可能很難翻譯該消息。擁有多種語言的SEO專家也將非常昂貴且難以擴展。在理想的情況下,特定於市場的SEO頁面可能應在母語中撰寫和優化,但這不再是翻譯過程的一部分。 next-multilingual的重點是提供一種簡單,簡化的解決方案,以將URL定位在許多語言中。
當使用next-multilingual/messages提供的getTitle API時, slug鍵也將用作title密鑰的後備。此API使SLUG感覺不足時可以輕鬆自定義標題。
配x 請注意,更改slug值意味著URL將會更改。由於這些更改正在next.config.js中發生,就像任何next.js配置更改一樣,必須重新啟動服務器才能查看生效的更改。如果更改文件夾結構,則同樣適用,因為基礎配置依賴於此。
如果您想擁有一個沒有任何頁面的目錄,則仍然可以通過創建index.<locale>.properties文件(其中locale是您支持的環境)。儘管支持此選項,但我們不建議使用它,因為這將使URL路徑更長,這違背了SEO最佳實踐。
默認情況下, next-multilingual將排除某些文件,例如自定義錯誤頁面或/api目錄下的任何API路由。在使用這些文件的消息時,您總是可以使用slug鍵,但是它們不會用於創建局部URL。
您可以隨時查看以查看“行動中的消息文件”的示例,但這裡是可以在首頁上使用的示例:
# Homepage title
exampleApp.homepage.title = Homepage
# Homepage headline
exampleApp.homepage.headline = Welcome to the homepage現在,我們學會瞭如何創建主頁以及有關工作方式的一些細節,我們可以輕鬆創建其他頁面。我們在示例中創建了許多頁面,但這裡是about-us.jsx樣本的示例:
import { NextPage } from 'next'
import { getTitle , useMessages } from 'next-multilingual/messages'
import Layout from '@/layout'
import styles from './index.module.css'
const AboutUs : NextPage = ( ) => {
const messages = useMessages ( )
const title = getTitle ( messages )
return (
< Layout title = { title } >
< h1 className = { styles . headline } > { title } </ h1 >
< p > { messages . format ( 'details' ) } </ p >
</ Layout >
)
}
export default AboutUs當然,您將擁有about-us.en-US.properties此消息文件:
# Page localized URL segment (slug) in (translatable) human readable format.
# This key will be "slugified" (e.g, "About Us" will become "about-us"). All non-alphanumeric characters will be replaced by "-".
exampleApp.aboutUsPage.slug = About Us
# Page details.
exampleApp.aboutUsPage.details = This is just some english boilerplate text.next-multilingual帶有其自己的<Link>組件,可允許客戶端和服務器端渲染局部URL。它的用法很簡單,它的工作原理與Next.js' <Link>完全一樣。
要記住的唯一重要的事情是, href屬性應始終包含下一個url。含義, pages文件夾下的文件結構應為使用的文件,而不是本地化版本。
換句話說,文件結構被認為是“非定位” URL表示形式,如果<link>與結構不同, <Link>將用本地化版本(來自消息文件)來替換URL。
API可在next-multilingual/link下使用,您可以這樣使用:
import Link from 'next-multilingual/link'
import { useMessages } from 'next-multilingual/messages'
const Menu : React . FC = ( ) => {
const messages = useMessages ( )
return (
< nav >
< Link href = "/" >
< a > { messages . format ( 'home' ) } </ a >
</ Link >
< Link href = "/about-us" >
< a > { messages . format ( 'aboutUs' ) } </ a >
</ Link >
< Link href = "/contact-us" >
< a > { messages . format ( 'contactUs' ) } </ a >
</ Link >
</ nav >
)
}
export default Menu當該頁面的消息文件中指定slug鍵時,這些鏈接中的每一個都將自動本地化。例如,在美國英語中, /en-us/contact-us而在加拿大法國人則將是/fr-ca/nous-joindre 。
由於此映射的數據在渲染過程中沒有立即可用, next-multilingual/link/ssr將負責服務器端渲染(SSR)。通過使用next-multilingual/config的getConfig ,將自動添加WebPack配置。如果您使用的是高級Config方法,則說明了為什麼在先前提供的示例中需要特殊的WebPack配置。
並非所有局部URL都使用<Link>組件,這也是為什麼Next.js具有router.push的原因。 next-multilingual可以用useLocalizedUrl Hook支持這些用例,該用例將返回局部URL,可由任何組件使用。這是一個如何利用它的示例:
import { NextPage } from 'next'
import { useMessages } from 'next-multilingual/messages'
import { useLocalizedUrl } from 'next-multilingual/url'
import router from 'next/router'
const Tests : NextPage = ( ) => {
const messages = useMessages ( )
const localizedUrl = useLocalizedUrl ( '/about-us' )
return < button onClick = { ( ) => router . push ( localizedUrl ) } > { messages . format ( 'clickMe' ) } </ button >
}
export default Tests如果您更喜歡在組件的頂部定義內聯網站,或者需要進行更高級的URL操作,也可以使用返回函數獲取URL的useGetLocalizedUrl Hook:
import { NextPage } from 'next'
import { useMessages } from 'next-multilingual/messages'
import { useGetLocalizedUrl } from 'next-multilingual/url'
import router from 'next/router'
const Tests : NextPage = ( ) => {
const messages = useMessages ( )
const { getLocalizedUrl } = useGetLocalizedUrl ( )
return (
< button onClick = { ( ) => router . push ( getLocalizedUrl ( '/about-us' ) ) } >
{ messages . format ( 'clickMe' ) }
</ button >
)
}
export default Tests請小心,如果要使用React元素中URL的字符串值,則會出現錯誤,因為預渲染和瀏覽器之間的URL不同。這樣做的原因是,在客戶端,第一次渲染上,next.js無法訪問重寫數據,因此使用“半定位”的URL路徑(例如
/fr-ca/about-us)。由於這是本機的next.js行為,因此目前解決此問題的最簡單方法是將suppressHydrationWarning={true}添加到您的元素中。為了解決此問題,useGetLocalizedUrl還返回一個可以用於跟踪何時可在客戶端上使用的局部URL的isLoading屬性。
您可能會遇到您還需要獲取本地化URL的情況,但是使用掛鉤不是一個選擇。這就是next-multilingual/url中getLocalizedUrl所在地。它的作用與useLocalizedUrl相同,但其locale論點是強制性的。
想像一下,請使用Next.js的API發送交易電子郵件,並希望利用next-multilingual的本地化URL,而無需將其用於配置中。這是如何使用它的示例:
import type { NextApiRequest , NextApiResponse } from 'next'
import { isLocale } from 'next-multilingual'
import { getMessages } from 'next-multilingual/messages'
import { getLocalizedUrl } from 'next-multilingual/url'
import { sendEmail } from 'send-email'
/**
* The "/api/send-email" handler.
*/
const handler = ( request : NextApiRequest , response : NextApiResponse ) : Promise < void > => {
const locale = request . headers [ 'accept-language' ]
let emailAddress = ''
try {
emailAddress = JSON . parse ( request . body ) . emailAddress
} catch ( error ) {
response . status ( 400 )
return
}
if ( locale === undefined || ! isLocale ( locale ) || ! emailAddress . length ) {
response . status ( 400 )
return
}
const messages = getMessages ( locale )
sendEmail (
emailAddress ,
messages . format ( 'welcome' , { loginUrl : await getLocalizedUrl ( '/login' , locale , true ) } )
)
response . status ( 200 )
}
export default handler創建組件與頁面相同,但它們生活在pages目錄之外。另外, slug鍵(如果使用)不會對URL產生任何影響。我們有一些應該是自我解釋的示例組件,但以下是Footer.tsx的示例。 TSX組件:
import { useMessages } from 'next-multilingual/messages'
const Footer : React . FC = ( ) => {
const messages = useMessages ( )
return < footer > { messages . format ( 'footerMessage' ) } </ footer >
}
export default Footer及其消息文件:
# This is the message in the footer at the bottom of pages
exampleApp.footerComponent.footerMessage = © Footer另外,請確保查看所有多語言應用程序中必須是必須的語言切換器組件示例。
我們已經很清楚,共享消息是從一開始的不良習慣,那麼我們在這裡談論什麼?實際上,共享消息本身還不錯。可能導致問題的是您在不同上下文中共享消息時。例如,您可能會很想noButton一個Button.ts yesButton在許多語言中,即使是按鈕,諸如“是”和“否”之類的簡單單詞也可以根據上下文具有不同的拼寫。
分享消息什麼時候好?對於項目列表。
例如,要使您的本地化過程保持簡單,您希望避免在數據庫中存儲可本質的字符串(有關為什麼在設計決策文檔中的更多詳細信息)。在您的數據庫中,您將使用唯一標識符識別上下文,然後將消息存儲在共享消息文件中,其中密鑰的標識符將與數據庫中的標識符匹配。
為了說明這一點,我們使用水果創建了一個示例。您需要做的就是創建一個調用這樣的useMessages鉤子:
export { useMessages as useFruitsMessages } from 'next-multilingual/messages'❗如果您需要在掛鉤之外訪問您的消息,則還需要導出
getMessages。
當然,您將在同一目錄中使用消息文件:
exampleApp.fruits.banana = Banana
exampleApp.fruits.apple = Apple
exampleApp.fruits.strawberry = Strawberry
exampleApp.fruits.grape = Grape
exampleApp.fruits.orange = Orange
exampleApp.fruits.watermelon = Watermelon
exampleApp.fruits.blueberry = Blueberry
exampleApp.fruits.lemon = Lemon要使用它,請簡單地從您可能需要以下值的任何地方導入此鉤子:
import { useFruitsMessages } from '../messages/useFruitsMessages'
const FruitList : React . FC ( ) => {
const fruitsMessages = useFruitsMessages ( )
return (
< >
{ fruitsMessages
. getAll ( )
. map ( ( message ) => message . format ( ) )
. join ( ', ' ) }
</ >
)
}
export default FruitList您也可以調用這樣的單個消息:
fruitsMessages . format ( 'banana' )分享這些項目列表的想法是,您可以在不同組件之間具有一致的經驗。想像一下,一個頁面中的水果列表,在另一頁中有一個自動完成輸入的下拉列表。但是要記住的重要部分是,該列表必須始終在相同的上下文中使用,而不是在不同上下文中重新使用某些消息。
在消息中使用佔位符是關鍵功能,因為並非所有消息都包含靜態文本。 next-multilingual of Box中支持ICU MessageFormat語法,這意味著您可以使用以下消息:
exampleApp.homepage.welcome = Hello {name}!並使用以下方式註入值:
messages . format ( 'welcome' , { name : 'John Doe' } ) format使用format時,有一些簡單的規則要牢記:
values參數,則它將簡單地將消息作為靜態文本輸出。values參數,則必須在消息中使用{placeholder}語法的所有佔位符的值。否則將不會顯示該消息。values ,它們將被默默地忽略。 ICU MessageFormat的主要好處之一是使用Unicode的工具和標準,以使應用程序能夠流利的大多數語言。許多工程師可能會認為,通過有2條消息,一條消息是單數,一個用於復數足以保持所有語言的流利。實際上,Unicode記錄了200多種語言的複數規則,而阿拉伯語(例如阿拉伯語)的某些語言最多可以具有6種複數形式。
為了確保您的句子能保持流利的所有語言,您可以使用以下消息:
exampleApp.homepage.mfPlural = {count, plural, =0 {No candy left.} one {Got # candy left.} other {Got # candies left.}}並使用Unicode定義的正確複數類別選擇正確的複數形式:
messages . format ( 'mfPlural' , { count } )關於這個話題有很多需要學習的東西。確保閱讀Unicode文檔,並自己嘗試該語法,以更加熟悉這種肆意的I18N功能。
在極少數情況下,您需要使用{placeholder}語法並在消息中顯示{ and }字符的兩個佔位符,您將需要由{ (for { )和} (for } )由這樣的翻譯工具識別的HTML實體:
exampleApp.debuggingPage.variableInfo = Your variable contains the following values: & # x7b;{values}}如果您有沒有值(佔位符)的消息,則不需要使用HTML實體逃脫{ and } ,並且將顯示實體作為靜態文本。
這是一個非常普遍的情況,我們需要在一條消息中具有內聯html。一種做到這一點的方法是:
# Bad example, do not ever do this!
exampleApp.homepage.createAccount1 = Please
exampleApp.homepage.createAccount2 = create your account
exampleApp.homepage.createAccount3 = today for free.進而:
< div >
{ messages . format ( 'createAccount1' ) }
< Link href = "/sign-up" > { messages . format ( 'createAccount2' ) } </ Link >
{ messages . format ( 'createAccount3' ) }
</ div >這種方法有兩個問題:
這實際上是一種稱為串聯的反圖案,應始終避免。這是使用formatJsx正確方法:
exampleApp.homepage.createAccount = Please <link>create your account</link> today for free.進而:
< div > { messages . formatJsx ( 'createAccount' , { link : < Link href = "/sign-up" > </ Link > } ) } </ div > formatJsx formatJsx支持佔位符和JSX元素作為values ,這意味著您可以在註入JSX元素時從標準format功能(例如復數)中受益。
使用format時,有一些簡單的規則要牢記:
formatJsx的參數。<link> XML標籤,需要使用link: <Link href="/"></Link> 。<i> ,也需要創建諸如<i1> , <i2>等的唯一標籤,並將其值作為JSX元素傳遞。Hello <name>{name}</name> )。.properties文件中。<Link href="/contact-us><a id="test"></a></Link>是有效的,但是<div><span1></span1><span2></span2></div>是無效的。相反,您必須在.properties文件中使用相同級別的XML標記,而不是JSX參數。 <和>使用formatJsx時,如果要將其顯示為文本,您仍然需要逃脫捲曲括號。此外,由於我們將在formatJsx消息中使用XML,因此類似的規則將適用於用於識別標籤的<和> 。
在一個罕見的事件中,您需要使用<element></element> (xml)語法將JSX注入消息中,並且還需要在消息中顯示<和>字符,您將需要由< (for < )和> (對於> )通過這樣的翻譯工具識別的HTML實體:
exampleApp.statsPage.targetAchieved = You achieved your weekly target (& # x3c;5) and are eligible for a <link>reward</link>.錨點鏈接是將您帶到文檔中的特定位置而不是頂部的鏈接。
next-multilingual的核心功能之一是支持本地化的URL。我們的設計是使用易於本地化然後轉換為SEO友好型slugs的普通句子建造的。我們可以使用相同的函數來劃分錨點鏈接,因此您可以擁有/fr-ca /fr-ca/nous-joindre#notre-équipe /fr-ca/nous-joindre#our-team 。
有兩種類型的錨鏈接:
如果錨點鏈接在同一頁面上,而在任何其他頁面上都沒有引用,則可以在此類頁面中僅將它們添加到.properties文件中:
# Table of content header
exampleApp.longPage.tableOfContent = Table of Content
# This key will be used both as content and "slugified". Make sure when translating that its value is unique.
exampleApp.longPage.p1Header = Paragraph 1
# "Lorem ipsum" text to make the (long) page scroll
exampleApp.longPage.p1 = Lorem ipsum dolor sit amet...然後,頁面可以使用slugify函數鏈接到與要指向URL片段相關的元素關聯的唯一標識符:
import { NextPage } from 'next'
import Link from 'next-multilingual/link'
import { slugify , useMessages } from 'next-multilingual/messages'
import { useRouter } from 'next/router'
const LongPage : NextPage = ( ) => {
const messages = useMessages ( )
const { locale } = useRouter ( )
return (
< div >
< div >
< h2 > { messages . format ( 'tableOfContent' ) } </ h2 >
< ul >
< li >
< Link href = { `# ${ slugify ( messages . format ( 'p1Header' ) , locale ) } ` } >
{ messages . format ( 'p1Header' ) }
</ Link >
</ li >
</ ul >
</ div >
< div >
< h2 id = { slugify ( messages . format ( 'p1Header' ) , locale ) } > { messages . format ( 'p1Header' ) } </ h2 >
< p > { messages . format ( 'p1' ) } </ p >
</ div >
</ div >
)
}
export default LongPage 在跨頁面上使用錨點鏈接也很常見,因此當您單擊鏈接時,瀏覽器將直接在該頁面上顯示相關內容。為此,您需要通過添加此簡單的導出將與“共享消息”相似的簡單導出來使頁面的消息可用於其他頁面:
export const useLongPageMessages = useMessages然後,您可以從其他頁面中使用此鉤子:
import { NextPage } from 'next'
import Link from 'next-multilingual/link'
import { slugify , useMessages } from 'next-multilingual/messages'
import { useRouter } from 'next/router'
import { useLongPageMessages } from './long-page'
const AnchorLinks : NextPage = ( ) => {
const messages = useMessages ( )
const { locale , pathname } = useRouter ( )
const longPageMessages = useLongPageMessages ( )
return (
< div >
< div >
< Link
href = { ` ${ pathname } /long-page# ${ slugify ( longPageMessages . format ( 'p3Header' ) , locale ) } ` }
>
{ messages . format ( 'linkAction' ) }
</ Link >
</ div >
</ div >
)
}
export default AnchorLinks此模式也適用於組件。這樣做的好處是,如果刪除或重構頁面,與之關聯的錨鏈接將始終與頁面保持聯繫。
您可以僅為錨點鏈接創建一個單獨的共享消息組件,但這將破壞接近原則。
示例鏈接的完整示例可以在示例應用程序中找到。
Next中缺少的一個功能是管理用於SEO的重要HTML標籤。我們添加了<Head>組件來處理HTML <head>中的兩個非常重要的標籤:
<link rel=canonical> ):這告訴搜索引擎,瀏覽頁面的真相來源是此URL。避免對重複內容受到懲罰非常重要,尤其是因為URL不敏感,但是Google將其視為病例敏感。<link rel=alternate> ):這告訴搜索引擎,瀏覽頁面也可以使用其他語言可用,並促進了網站的爬行。 API可在next-multilingual/head下使用,您可以這樣導入:
import Head from 'next-multilingual/head'就像<Link>一樣, <Head>是next.js' <Head>組件的倒數替換。在我們的示例中,我們在佈局組件中使用它:這樣:
< Head >
< title > { title } </ title >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" > </ meta >
</ Head >所有這些都是插入規範和替代鏈接,以便搜索引擎可以更好地爬網。例如,如果您在/en-us/about-us頁面上,則下列HTML將在您的HTML <head>標籤下自動添加:
< link rel =" canonical " href =" http://localhost:3000/en-us/about-us " />
< link rel =" alternate " href =" http://localhost:3000/en-us/about-us " hreflang =" en-US " />
< link rel =" alternate " href =" http://localhost:3000/fr-ca/%C3%A0-propos-de-nous " hreflang =" fr-CA " />為了完全受益於SEO標記, <Head>必須包含在所有頁面上。有多種方法可以實現這一目標,但是在示例中,我們創建了在所有頁面上使用的<Layout>組件。
像大多數站點一樣,您將需要利用Next.js的自定義錯誤頁面功能。使用useMessages() ,它與創建任何其他頁面一樣容易。例如,對於404錯誤,您可以創建404.tsx :
import { NextPage } from 'next'
import Link from 'next-multilingual/link'
import { getTitle , useMessages } from 'next-multilingual/messages'
import Layout from '@/layout'
const Error404 : NextPage = ( ) => {
const messages = useMessages ( )
const title = getTitle ( messages )
return (
< Layout title = { title } >
< h1 > { title } </ h1 >
< Link href = "/" >
< a > { messages . format ( 'goBack' ) } </ a >
</ Link >
</ Layout >
)
}
export default Error404當然,您的消息,例如404.en-US.properties :
# Page title
exampleApp.pageNotFoundError.title = 404 - Page Not Found
# Go back link text
exampleApp.pageNotFoundError.goBack = Go back homeAPI通常需要進行本地化。這是一個“ Hello API”示例:
import type { NextApiRequest , NextApiResponse } from 'next'
import { getMessages } from 'next-multilingual/messages'
/**
* Example API schema.
*/
type Schema = {
message : string
}
/**
* The "hello API" handler.
*/
const handler = ( request : NextApiRequest , response : NextApiResponse < Schema > ) : void => {
const locale = request . headers [ 'accept-language' ]
if ( locale === undefined || ! isLocale ( locale ) ) {
response . status ( 400 )
return
}
const messages = getMessages ( locale )
response . status ( 200 ) . json ( { message : messages . format ( 'message' ) } )
}這與示例應用程序中實現的API非常相似。我們正在使用Accept-Language HTTP標頭來告訴API我們希望在哪個語言環境中進行響應。與具有當前語言環境的上下文的useMessages掛鉤不同,我們需要告訴getMessages在哪個語言環境中返回消息。
消息文件的行為與使用useMessages完全相同,您只需要在API路由文件旁邊創建一個,在我們的情況下, hello.en-US.properties :
# API message
exampleApp.helloApi.message = Hello, from API.您可以像任何其他基於React的API調用一樣,在任何頁面中實現此信息,例如:
const SomePage : NextPage = ( ) => {
const [ apiError , setApiError ] = useState ( null )
const [ isApiLoaded , setApiIsLoaded ] = useState ( false )
const [ apiMessage , setApiMessage ] = useState ( '' )
useEffect ( ( ) => {
setApiIsLoaded ( false )
const requestHeaders : HeadersInit = new Headers ( )
requestHeaders . set ( 'Accept-Language' , normalizeLocale ( router . locale as string ) )
fetch ( '/api/hello' , { headers : requestHeaders } )
. then ( ( result ) => result . json ( ) )
. then (
( result ) => {
setApiIsLoaded ( true )
setApiMessage ( result . message )
} ,
( apiError ) => {
setApiIsLoaded ( true )
setApiError ( apiError )
}
)
} , [ router . locale ] )
const showApiMessage : React . FC = ( ) => {
if ( apiError ) {
return (
< >
{ messages . format ( 'apiError' ) }
{ ( apiError as Error ) . message }
</ >
)
} else if ( ! isApiLoaded ) {
return < > { messages . format ( 'apiLoading' ) } </ >
} else {
return < > { apiMessage } </ >
}
}
return (
< div >
< h2 > { messages . format ( 'apiHeader' ) } </ h2 >
< div > { showApiMessage ( { } ) } </ div >
</ div >
)
} normalizeLocale不是強制性的,而是建議的ISO 3166慣例。由於Next.js將erentes用作URL前綴,因此它們在配置中較低,並且可以根據需要重新歸一化。
❗動態路線很複雜,並且將它們定位增加了更複雜的性能。在嘗試添加本地化之前,請確保您熟悉this.js功能的工作方式。
動態路線非常普遍,並在下一步受到支持。自3.0版以來, next-multilingual就動態路線提供了與Next.js相同的支持。為了使動態路線與next-multilingual一起使用,我們有幾種模式可供遵循:
<Link>的href屬性和useLocalizedUrl / useGetLocalizedUrl / getLocalizedUrl url參數僅接受字符串URL。UrlObject Next.js' <Link>組件不同,我們更喜歡簡化我們的類型,因為可以輕鬆地使用urlObject.href 。userRouter().asPath (最常見的情況)。通過使用asPath您可以使用本地化的URL,這意味著您將使用的URL將被完全局部化。userRouter().pathname是通過提供局部參數與hydrateRouteParameters的結合。通過使用pathname ,您正在使用非定位的URL,這意味著您將使用的URL可能是非定位段以及本地化參數的混合。在您具有嵌套動態路由的情況下,這可能很有用。我們在動態路線測試頁面中提供了幾個ON使用動態路線的示例。
動態路線的主要挑戰是,如果需要本地化參數的值,我們需要保持語言之間的關係,以便我們可以正確切換語言。 next-multilingual通過其getLocalizedRouteParameters API解決了這個問題,該API創建了用作頁面props的LocalizedRouteParameters對象。這可以與getStaticProps和getServerSideProps一起使用。
getStaticProps示例首先,您需要告訴Next.js哪些預定義路徑將通過使用getStaticPaths有效(在第一個示例中添加了所有導入):
import { getCitiesMessages } from '@/messages/cities/citiesMessages'
import { GetStaticPaths } from 'next'
import { slugify } from 'next-multilingual/messages'
export const getStaticPaths : GetStaticPaths = async ( context ) => {
const paths : MultilingualStaticPath [ ] = [ ]
const { locales } = getStaticPathsLocales ( context )
locales . forEach ( ( locale ) => {
const citiesMessages = getCitiesMessages ( locale )
citiesMessages . getAll ( ) . forEach ( ( cityMessage ) => {
paths . push ( {
params : {
city : slugify ( cityMessage . format ( ) , locale ) ,
} ,
locale ,
} )
} )
} )
return {
paths ,
fallback : false ,
}
}然後,您必須預先計算本地化路由參數,並使用getStaticProps和getLocalizedRouteParameters作為道具返回它們:
export type CityPageProps = { localizedRouteParameters : LocalizedRouteParameters }
export const getStaticProps : GetStaticProps < CityPageProps > = async ( context ) => {
const localizedRouteParameters = getLocalizedRouteParameters (
context ,
{
city : getCitiesMessages ,
} ,
import . meta . url
)
return { props : { localizedRouteParameters } }
}如果您使用的是全部動態路線,則需要將參數作為數組傳遞,對於要支持的每個URL段。例如,如果您想支持2個級別:
const localizedRouteParameters = getLocalizedRouteParameters ( context , {
city : [ getCitiesMessages , getCitiesMessages ] ,
} )請注意,由於我們需要使用getMessages API而不是useMessages鉤子,因此您還需要在消息文件中導出它:
export {
getMessages as getCitiesMessages ,
useMessages as useCitiesMessages ,
} from 'next-multilingual/messages'最後,當您創建頁面時,您必須將本地化路由參數傳遞到語言切換器組件:
const CityPage : NextPage < CityPageProps > = ( { localizedRouteParameters } ) => {
const messages = useMessages ( )
const title = getTitle ( messages )
const { query } = useRouter ( )
return (
< Layout title = { title } localizedRouteParameters = { localizedRouteParameters } >
< h1 > { query [ 'city' ] } < / h1>
< / Layout>
)
}
export default CityPage現在唯一缺少的部分是語言切換器,它需要使用getLanguageSwitcherUrl來利用本地化路由參數:
import { normalizeLocale , setCookieLocale } from 'next-multilingual'
import Link from 'next-multilingual/link'
import { KeyValueObject } from 'next-multilingual/messages'
import { LocalizedRouteParameters , useRouter } from 'next-multilingual/router'
import { getLanguageSwitcherUrl } from 'next-multilingual/url'
import { ReactElement } from 'react'
// Locales don't need to be localized.
const localeStrings : KeyValueObject = {
'en-US' : 'English (United States)' ,
'fr-CA' : 'Français (Canada)' ,
}
type LanguageSwitcherProps = {
/** Route parameters, if the page is using a dynamic route. */
localizedRouteParameters ?: LocalizedRouteParameters
}
export const LanguageSwitcher : React . FC < LanguageSwitcherProps > = ( { localizedRouteParameters } ) => {
const router = useRouter ( )
const { pathname , locale : currentLocale , locales , defaultLocale , query } = useRouter ( )
const href = getLanguageSwitcherUrl ( router , localizedRouteParameters )
return (
< div id = "language-switcher" >
< ul >
{ locales
. filter ( ( locale ) => locale !== currentLocale )
. map ( ( locale ) => {
return (
< li key = { locale } >
< Link href = { href } locale = { locale } >
< a
onClick = { ( ) => {
setCookieLocale ( locale )
} }
lang = { normalizeLocale ( locale ) }
>
{ localeStrings [ normalizeLocale ( locale ) ] }
< / a>
< / Link>
< / li >
)
} ) }
< / ul>
< / div >
)
}查看我們完全工作的示例:
我們理想的翻譯過程是您將修改後的文件發送到本地化供應商(在分支機構工作時),並在文件名中使用正確的語言環境恢復翻譯文件。一旦將文件退回,您基本上將它們提交到您的分支機構中,這意味著本地化成為開發過程中不可或缺的一部分。基本上,這個想法是:
在編寫本文檔時,我們沒有任何“導出/導入”工具可以提供幫助。
next-multilingual ? ? ️為什麼我們在這些細節上付出了太多努力?因為我們的假設是它可以對:
可以在每個API的單個讀數文件和文檔目錄中的單個讀數文件中找到有關實施和設計決策的更多詳細信息。