เนื่องจากการเปลี่ยนโฟกัสและลำดับความสำคัญเราไม่สามารถรักษาแพ็คเกจนี้ได้อีกต่อไป มันจะไม่ได้รับการอัปเดตการแก้ไขข้อบกพร่องหรือคุณสมบัติใหม่และอาจไม่เข้ากันเมื่อเวลาผ่านไป เราขอแนะนำให้เปลี่ยนไปใช้แพ็คเกจที่รองรับไดเรกทอรีแอป Next.js ล่าสุด
next-multilingual เป็นโซลูชันแบบ end-to-end ที่มีความคิดเห็นสำหรับแอปพลิเคชัน next.js ✱ ที่ต้องใช้หลายภาษา
ตรวจสอบแอพสาธิตของเรา!
✱ การใช้งาน next-multilingual ไปใช้งานได้เท่านั้น? ไดเรกทอรี pages เรายังคงรีดโซลูชันของเราเพื่อรองรับสิ่งใหม่หรือไม่? ไดเรกทอรี app เนื่องจากการทำให้เป็นสากลไม่ได้เป็นส่วนหนึ่งของการกำหนดค่า Next.js อีกต่อไป
npm install next-multilingual
useMessages ทรงพลังที่รองรับ ICU MessageFormat และ JSX Injection นอกกรอบ/en-us/contact-us สำหรับสหรัฐอเมริกาและ /fr-ca/nous-joindre สำหรับแคนาดาฝรั่งเศส) ✱ slug locale locale เริ่มต้นของคุณจะต้องตรงกับระบบไฟล์ไดเรกทอรี pages ( เช่นกระสุนสำหรับ "เกี่ยวกับเรา" ควรอยู่ในไดเรกทอรี about-us ) หากสถานที่เริ่มต้นที่คุณต้องการใช้อักขระที่เกินกว่าที่ระบบไฟล์รองรับจะไม่ได้รับการทดสอบและอาจไม่ทำงาน ยินดีต้อนรับคำขอดึง?.
next-multilingual ได้ใช้ความพยายามอย่างมากในการเพิ่ม TSDOC ให้กับ API ทั้งหมด โปรดตรวจสอบโดยตรงใน IDE ของคุณหากคุณไม่แน่ใจว่าจะใช้ APIs บางอย่างที่ให้ไว้ในตัวอย่างของเราได้อย่างไร
นอกจากนี้การมีความเห็นเกี่ยวกับ "แนวปฏิบัติที่ดีที่สุด" ไม่ใช่เรื่องง่าย นี่คือเหตุผลที่เราบันทึกการตัดสินใจออกแบบของเราในเอกสารพิเศษที่สามารถปรึกษาได้ที่นี่ หากคุณรู้สึกว่า API ของเราบางตัวไม่เสนอสิ่งที่คุณคาดหวังให้แน่ใจว่าได้ปรึกษาเอกสารนี้ก่อนที่จะเปิดปัญหา
สำหรับผู้ที่ต้องการกระโดดเข้าสู่การกระทำให้ดูที่ไดเรกทอรี example สำหรับการใช้งานแบบครบวงจรของ next-multilingual สำหรับส่วนที่เหลือส่วนด้านล่างจะให้คำแนะนำการกำหนดค่าทีละขั้นตอนทีละขั้นตอน
มีตัวเลือกมากมายในการกำหนดค่าใน next.js เพื่อให้บรรลุเป้าหมายของเรา next-multilingual ส่วนใหญ่ใส่ใจเกี่ยวกับ:
เราเสนอ API สองตัวเพื่อทำให้ขั้นตอนนี้ง่ายขึ้น:
getConfig (การกำหนดค่าง่าย) ฟังก์ชั่นนี้จะสร้างการกำหนดค่า next.js ที่จะตรงกับกรณีการใช้งานส่วนใหญ่ getConfig ใช้อาร์กิวเมนต์ต่อไปนี้:
applicationId - ตัวระบุแอปพลิเคชันที่ไม่ซ้ำกันซึ่งจะใช้เป็นคำนำหน้าคีย์ข้อความlocales - สถานที่ของแอปพลิเคชันของคุณdefaultLocale - สถานที่เริ่มต้นของแอปพลิเคชันของคุณ (จะต้องรวมอยู่ใน locales )❗เฉพาะแท็กภาษา BCP 47 ตามรูป
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-multilingual ไปโดยตรงใน next.config.js ที่มีอยู่ อาร์กิวเมนต์ของ 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
} ความสามารถในการกำหนดเส้นทางปัจจุบันของการใช้งาน 2 แบบ next-multilingual/config
next-multilingual/config Head จัดการการกำหนดค่า webpack พิเศษที่จำเป็นสำหรับการแสดงผลด้านเซิร์ฟเวอร์ของ URL ที่มีการแปลโดยใช้ภาษา next-multilingual/head/ssr next-multilingual/link/ssr สำหรับส่วนประกอบ Link
สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการใช้งานเช่นสาเหตุที่เราใช้อักขระ UTF-8 โปรดดูเอกสารการตัดสินใจออกแบบ
next-multilingual/messages/babel-plugin ในการแสดงข้อความที่แปลเป็นภาษาท้องถิ่นด้วย hook useMessages() เราจำเป็นต้องกำหนดค่าปลั๊กอิน Babel ที่กำหนดเองของเราซึ่งจะฉีดสตริงลงในหน้าและส่วนประกอบโดยอัตโนมัติ วิธีที่แนะนำในการทำเช่นนี้คือการรวม .babelrc ที่ฐานของแอปพลิเคชันของคุณ:
{
"presets" : [ " next/babel " ],
"plugins" : [ " next-multilingual/messages/babel-plugin " ]
} หากคุณไม่ได้กำหนดค่าปลั๊กอินคุณจะได้รับข้อผิดพลาดเมื่อพยายามใช้ useMessages
App ที่กำหนดเอง ( _app.tsx ) เราจำเป็นต้องสร้าง App ที่กำหนดเองโดยเพิ่ม _app.tsx ในไดเรกทอรี pages :
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 ) นอกจากนี้เรายังต้องสร้าง Document ที่กำหนดเองโดยการเพิ่ม _document.tsx ในไดเรกทอรี pages :
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 ที่มีคุณสมบัติครบถ้วน หากคุณใช้ basePath ของ next.js มันจะถูกเพิ่มเข้าไปใน URL พื้นฐานโดยอัตโนมัติ
NEXT_PUBLIC_ORIGIN จะยอมรับโดเมนที่มีคุณสมบัติครบถ้วนเท่านั้น (เช่น http://example.com ) โดยไม่มีเส้นทางใด ๆ
next-multilingual ? ตอนนี้ทุกอย่างได้รับการกำหนดค่าแล้วเราสามารถมุ่งเน้นไปที่การใช้ next-multilingual !
เพื่อให้ได้ next-multilingual ในการทำงานตามที่ออกแบบมาเราต้องหาวิธีแก้ปัญหา 2 ปัญหา:
undefined : เนื่องจาก next.js สนับสนุนไซต์ที่ไม่มีสถานที่นั้นประเภทดั้งเดิมอนุญาตให้มีค่า undefined ซึ่งสำหรับกรณีของเราเป็นเรื่องน่ารำคาญและต้องมีการหล่อพิเศษnext-multilingual ต้องสร้างสถานที่เริ่มต้นที่เราไม่เคยใช้ ซึ่งหมายความว่าในการเข้าถึงข้อมูลสถานที่ที่เกี่ยวข้องเราไม่สามารถพึ่งพา APIs ของ Js Next.js ได้เราสร้าง APIs ต่อไปนี้เพื่ออนุญาตค่าตำแหน่งที่สอดคล้องกันในแอปพลิเคชันของคุณ:
useRouter นี่คือ wrapper ง่าย ๆ ที่ด้านบนของ useRouter next.js ซึ่งทั้งคู่มีสถานที่ที่ถูกต้อง แต่ยังไม่เคยส่งคืน 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 : { } }
}
โปรดทราบว่าในขณะที่เราแนะนำให้ใช้การตรวจจับสถานที่อัจฉริยะเพื่อแสดงผลแบบไดนามิกของหน้าแรก แต่นี่เป็นตัวเลือกที่สมบูรณ์ ด้วยการใช้การกำหนดค่าขั้นสูงกับ localeDetection: trueคุณจะเรียกคืนพฤติกรรมเริ่มต้น next.js โดยไม่จำเป็นต้องใช้getServerSideProps
หน้าแรกมีความซับซ้อนมากกว่าหน้าอื่น ๆ เล็กน้อยเนื่องจากเราจำเป็นต้องใช้การตรวจจับสถานที่ตั้งแบบไดนามิก (และจอแสดงผล) ด้วยเหตุผลดังต่อไปนี้:
/ สามารถส่งผลกระทบเชิงลบต่อ 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 (คอมไพล์ได้) และคุณต้องการข้อความที่แปลเป็นภาษาท้องถิ่นคุณสามารถสร้างไฟล์ข้อความในสถานที่ที่รองรับซึ่งจะใช้งานได้โดยไฟล์เหล่านี้เท่านั้น เช่นเดียวกับโมดูล CSS แนวคิดคือคุณสามารถมีไฟล์ข้อความที่เกี่ยวข้องกับขอบเขตท้องถิ่นของไฟล์อื่น สิ่งนี้มีประโยชน์ในการสร้างข้อความมากขึ้นและยังหลีกเลี่ยงการแบ่งปันข้อความในบริบทที่แตกต่างกัน (รายละเอียดเพิ่มเติมในเอกสารการตัดสินใจออกแบบว่าทำไมสิ่งนี้ไม่ดี)
ไฟล์ข้อความมี 2 กรณีการใช้งานหลัก:
pages ของคุณคุณสามารถระบุเซ็กเมนต์ URL ที่มีการแปล (ส่วนหนึ่งของ URL ในระหว่าง / หรือที่ส่วนท้ายของเส้นทาง) โดยใช้ตัวระบุคีย์ slug รายละเอียดเพิ่มเติมเกี่ยวกับวิธีการทำสิ่งนี้ด้านล่างuseMessages ลองนึกภาพ CSS แต่สำหรับสตริงที่มีความสามารถพิเศษเพื่อสรุป:
การสร้างและการจัดการไฟล์เหล่านั้นนั้นง่ายพอ ๆ กับการสร้างแผ่นสไตล์ แต่นี่คือรายละเอียดที่สำคัญ:
.properties ใช่คุณอาจสงสัยว่าทำไม แต่มีเหตุผลที่ดีที่บันทึกไว้ในเอกสารการตัดสินใจออกแบบUTF-8 การไม่ทำเช่นนั้นจะแทนที่อักขระที่ไม่ใช่ละติน �.properties เราทำตามการตั้งชื่อที่เข้มงวด: <PageFilename>.<locale>.properties<applicationId>.<context>.<id> ที่ไหน:next-multilingual/configaboutUsPage หรือ footerComponent อาจเป็นตัวอย่างที่ดีของบริบท แต่ละไฟล์สามารถมีเพียง 1 บริบทและบริบทที่ไม่ควรใช้ในหลาย ๆ ไฟล์เนื่องจากอาจทำให้เกิด "การชนคีย์" (คีย์ที่ไม่ใช่ Unique). และสามารถมีอักขระตัวอักษรและตัวเลขระหว่าง 1 ถึง 50 ตัวเท่านั้น - เราขอแนะนำให้ใช้เคสอูฐเพื่อการอ่านslugtitlegetTitle API ที่ให้ไว้ใน next-multilingual/messages เพื่อการย้อนกลับระหว่าง title และปุ่ม slug โดยอัตโนมัติuseMessagesนอกจากนี้ตรวจสอบให้แน่ใจว่าได้ตรวจสอบบันทึกคอนโซลของคุณสำหรับคำเตือนเกี่ยวกับปัญหาที่อาจเกิดขึ้นกับข้อความของคุณ มันอาจเป็นเรื่องยากที่จะคุ้นเคยกับการทำงานก่อน แต่เราพยายามทำให้ง่ายต่อการตรวจจับและแก้ไขปัญหา โปรดทราบว่าบันทึกเหล่านั้นจะแสดงเฉพาะในสภาพแวดล้อมที่ไม่ใช่การผลิต
ไม่ใช่เรื่องแปลกที่จะต้องมีข้อความที่แปลเป็นภาษาท้องถิ่นในขณะที่ไม่สามารถใช้ตะขอได้ ตัวอย่างจะเป็นในขณะที่ใช้คุณสมบัติหลักของ Next.js อย่างใดอย่างหนึ่งคือการรองรับ API ในตัว ในบริบทนั้นแทนที่จะใช้ useMessage เราสามารถใช้ getMessages ในขณะที่ระบุอาร์กิวเมนต์ locale
ดังที่ได้กล่าวไว้ก่อนหน้านี้มีคีย์พิเศษหนึ่งคีย์สำหรับ pages ที่ id คือ slug ซึ่งแตกต่างจากทากแบบดั้งเดิมที่ดูเหมือนว่า this-is-a-page เราขอให้คุณเขียนกระสุนเป็นประโยคปกติและที่มนุษย์อ่านได้เพื่อให้สามารถแปลได้เหมือนสตริงอื่น ๆ สิ่งนี้หลีกเลี่ยงการมีกระบวนการพิเศษสำหรับกระสุนซึ่งอาจมีค่าใช้จ่ายสูงและซับซ้อนในการจัดการในหลายภาษา
โดยพื้นฐานแล้ว slug เป็น "คำอธิบายสั้น" ที่มนุษย์อ่านได้ของหน้าของคุณและแสดงถึงเซ็กเมนต์ (ส่วนหนึ่งระหว่าง / หรือที่ส่วนท้ายของเส้นทาง) ของ URL เมื่อใช้เป็นเซ็กเมนต์ URL จะใช้การแปลงต่อไปนี้:
- ตัวอย่างเช่น About Us จะกลายเป็น about-us
สำหรับโฮมเพจ URL จะเป็น / ซึ่งหมายความว่าคีย์ slug จะไม่ถูกใช้เพื่อสร้างส่วน URL ที่มีการแปล
อย่าลืมกระสุนจะต้องเขียนเป็นคำอธิบายสั้น ๆ ปกติซึ่งหมายถึงการข้ามคำเพื่อให้มันสั้นลงสำหรับ SEO นั้นหมดกำลังใจ เหตุผลหลักสำหรับเรื่องนี้คือถ้าคุณเขียน "คำหลัก" ซึ่งเป็นนักภาษาศาสตร์ที่ไม่คุ้นเคยกับ SEO อาจมีช่วงเวลาที่ยากลำบากในการแปลข้อความนั้น การมีผู้เชี่ยวชาญ SEO ในหลาย ๆ ภาษาก็มีค่าใช้จ่ายสูงและยากต่อการขยาย ในสถานการณ์ที่เหมาะสมที่สุดหน้า SEO เฉพาะตลาดควรได้รับการแต่งตั้งและปรับให้เหมาะสมในภาษาพื้นเมือง แต่นี่ไม่ใช่ส่วนหนึ่งของกระบวนการแปลอีกต่อไป โฟกัสของภาษา next-multilingual คือการจัดหาโซลูชันที่ง่ายและมีความคล่องตัวในการ จำกัด URL ในหลายภาษา
คีย์ slug จะถูกใช้เป็นทางเลือกของคีย์ title เมื่อใช้ getTitle API ที่ให้ไว้ใน next-multilingual/messages API นี้ทำให้ง่ายต่อการปรับแต่งชื่อเมื่อกระสุนรู้สึกไม่เพียงพอ
โปรดทราบว่าการเปลี่ยนค่า slugหมายความว่า URL จะเปลี่ยนไป เนื่องจากการเปลี่ยนแปลงเหล่านั้นเกิดขึ้นในnext.config.jsเช่นเดียวกับการเปลี่ยนแปลงการกำหนดค่า next.js ใด ๆ เซิร์ฟเวอร์จะต้องเริ่มต้นใหม่เพื่อดูการเปลี่ยนแปลงที่มีผล เช่นเดียวกันถ้าคุณเปลี่ยนโครงสร้างโฟลเดอร์เนื่องจากการกำหนดค่าพื้นฐานขึ้นอยู่กับสิ่งนี้
หากคุณต้องการมีไดเรกทอรีที่ไม่มีหน้าใด ๆ คุณยังสามารถ จำกัด วงใช้งาน locale โดยการสร้าง index.<locale>.properties ไฟล์. ในขณะที่ตัวเลือกนี้ได้รับการสนับสนุนเราไม่แนะนำให้ใช้เพราะจะทำให้เส้นทาง 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 "non-localized" และ <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 ในไฟล์ข้อความของหน้านั้น ตัวอย่างเช่นในสหรัฐอเมริกาภาษาอังกฤษเส้นทาง URL "ติดต่อเรา" จะเป็น /en-us/contact-us ในขณะที่ภาษาฝรั่งเศสแคนาดามันจะเป็น /fr-ca/nous-joindre
เนื่องจากข้อมูลสำหรับการแมปนี้ไม่สามารถใช้งานได้ทันทีในระหว่างการเรนเดอร์ next-multilingual/link/ssr จะดูแลการแสดงผลด้านเซิร์ฟเวอร์ (SSR) ด้วยการใช้ getConfig ของ next-multilingual/config การกำหนดค่า WebPack จะถูกเพิ่มโดยอัตโนมัติ หากคุณใช้วิธี Config ขั้นสูงสิ่งนี้จะอธิบายว่าทำไมการกำหนดค่า WebPack พิเศษจึงจำเป็นต้องใช้ในตัวอย่างที่ให้ไว้ก่อนหน้านี้
URL ที่มีการแปลทั้งหมดไม่ได้ใช้ส่วนประกอบ <Link> และนี่คือเหตุผลที่ next.js มีวิธีการ router.push ที่สามารถใช้งานได้โดยกรณีการใช้งานอื่น ๆ อีกมากมาย next-multilingual สามารถรองรับกรณีการใช้งานเหล่านี้ด้วย hook useLocalizedUrl ที่จะส่งคืน 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 ขั้นสูงมากขึ้นคุณสามารถใช้ hook useGetLocalizedUrl ที่ส่งคืนฟังก์ชั่นเพื่อรับ URL:
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ระวังหากคุณต้องการใช้ค่าสตริงของ URL ภายในองค์ประกอบ React คุณจะมีข้อผิดพลาดเนื่องจาก URL แตกต่างกันระหว่างการเรนเดอร์ก่อนและเบราว์เซอร์ เหตุผลนี้คือบนไคลเอนต์ในการเรนเดอร์ครั้งแรก next.js ไม่สามารถเข้าถึงข้อมูลการเขียนซ้ำและใช้เส้นทาง URL "กึ่งท้องถิ่น" (เช่น
/fr-ca/about-us) เนื่องจากนี่เป็นพฤติกรรม Next.js next.js วิธีที่ง่ายที่สุดในการแก้ไขปัญหานี้ในตอนนี้คือการเพิ่มsuppressHydrationWarning={true}ลงในองค์ประกอบของคุณ ในการแก้ไขปัญหานี้useGetLocalizedUrlยังส่งคืนคุณสมบัติisLoadingที่สามารถใช้ในการติดตามเมื่อ URL ที่มีการแปลมีให้ใช้กับไคลเอนต์
คุณอาจพบกับสถานการณ์ที่คุณต้องได้รับ URL ที่มีการแปล แต่การใช้ตะขอไม่ใช่ตัวเลือก นี่คือที่ getLocalizedUrl ใน next-multilingual/url เข้ามามันทำหน้าที่เหมือนกับ useLocalizedUrl แต่ข้อโต้แย้ง locale นั้นเป็นสิ่งจำเป็น
ลองนึกภาพการใช้ API ของ next.js เพื่อส่งอีเมลการทำธุรกรรมและต้องการใช้ประโยชน์จาก URL ที่มีการแปล next-multilingual โดยไม่ต้องใช้ hardcode ในการกำหนดค่า นี่คือตัวอย่างของวิธีการใช้งาน:
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 :
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ตรวจสอบให้แน่ใจว่าได้ดูตัวอย่างองค์ประกอบสวิตช์ภาษาที่เป็นสิ่งจำเป็นในแอปพลิเคชันหลายภาษา
เราได้ชัดเจนว่าการแบ่งปันข้อความเป็นวิธีปฏิบัติที่ไม่ดีตั้งแต่ต้นดังนั้นเรากำลังพูดถึงอะไรที่นี่? ในความเป็นจริงการแบ่งปันข้อความด้วยตัวเองไม่เลว สิ่งที่อาจทำให้เกิดปัญหาคือเมื่อคุณแบ่งปันข้อความในบริบทที่แตกต่างกัน ตัวอย่างเช่นคุณอาจถูก Button.ts ให้สร้างไฟล์ข้อความที่ใช้ร่วมกันที่มี yesButton , noButton Keys - แต่สิ่งนี้จะผิด ในหลายภาษาคำง่าย ๆ เช่น "ใช่" และ "ไม่" สามารถมีการสะกดที่แตกต่างกันขึ้นอยู่กับบริบทแม้ว่ามันจะเป็นปุ่มก็ตาม
เมื่อไหร่ที่จะแบ่งปันข้อความ? สำหรับรายการของรายการ
ตัวอย่างเช่นเพื่อให้กระบวนการโลคัลไลเซชั่นของคุณง่ายขึ้นคุณต้องการหลีกเลี่ยงการจัดเก็บสตริงที่มีความสามารถพิเศษในฐานข้อมูลของคุณ (รายละเอียดเพิ่มเติมเกี่ยวกับสาเหตุในเอกสารการตัดสินใจออกแบบ) ในฐานข้อมูลของคุณคุณจะระบุบริบทโดยใช้ตัวระบุที่ไม่ซ้ำกันและคุณจะเก็บข้อความของคุณไว้ในไฟล์ข้อความที่ใช้ร่วมกันซึ่งตัวระบุคีย์ของคุณจะตรงกับข้อความจากฐานข้อมูล
เพื่อแสดงสิ่งนี้เราได้สร้างตัวอย่างหนึ่งโดยใช้ผลไม้ สิ่งที่คุณต้องทำคือสร้างตะขอที่เรียกใช้ 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 รองรับไวยากรณ์ MessageFormat ICU ออกจากกล่องซึ่งหมายความว่าคุณสามารถใช้ข้อความต่อไปนี้:
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 ที่ต่ำกว่า Hyped นี้
ในเหตุการณ์ที่หายากซึ่งคุณจะต้องใช้ทั้งสองตัวยึดโดยใช้ไวยากรณ์ {placeholder} และแสดงอักขระ { และ } ในข้อความคุณจะต้องแทนที่ด้วย { (สำหรับ { ) และ } (สำหรับ } ) เอนทิตี HTML ซึ่งได้รับการยอมรับจากเครื่องมือแปลเช่นนี้:
exampleApp.debuggingPage.variableInfo = Your variable contains the following values: & # x7b;{values}} หากคุณมีข้อความที่ไม่มีค่า (ตัวยึดตำแหน่ง) การหลบหนี { และ } ไม่จำเป็นต้องใช้เอนทิตี HTML และจะแสดงเอนทิตีเป็นข้อความแบบคงที่
มันเป็นสถานการณ์ที่พบบ่อยมากที่เราต้องมี 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 >มี 2 ปัญหาเกี่ยวกับวิธีการนี้:
นี่คือการต่อต้านรูปแบบที่เรียกว่า การต่อกัน และควรหลีกเลี่ยงเสมอ นี่เป็นวิธีที่ถูกต้องในการทำเช่นนี้โดยใช้ 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 ซึ่งหมายความว่าคุณจะได้รับประโยชน์จากคุณสมบัติ format มาตรฐาน (เช่น plurals) ในขณะที่ฉีดองค์ประกอบ JSX
มีกฎง่ายๆสองสามข้อที่ต้องคำนึงถึงเมื่อใช้ format :
formatJsx<link> XML องค์ประกอบ JSX จะต้องมีให้โดยใช้ link: <Link href="/"></Link><i> หลายครั้งในประโยคคุณจะต้องสร้างแท็กที่ไม่ซ้ำกันเช่น <i1> , <i2> ฯลฯ และส่งผ่านค่าของพวกเขาในการโต้แย้งเป็นองค์ประกอบ JSXHello <name>{name}</name> ) ไม่รองรับ.properties เสมอ<Link href="/contact-us><a id="test"></a></Link> นั้นถูกต้อง แต่ <div><span1></span1><span2></span2></div> ไม่ถูกต้องแทนคุณต้องใช้มาร์กอัพ XML ระดับเดียวกันในไฟล์. .properties < และ > เมื่อใช้ formatJsx คุณจะต้องหลบหนีวงเล็บหยิกหากคุณต้องการแสดงเป็นข้อความ นอกจากนี้เนื่องจากเราจะใช้ XML ในข้อความ formatJsx กฎที่คล้ายกันจะใช้กับ < และ > ซึ่งใช้ในการระบุแท็ก
ในเหตุการณ์ที่หายากที่คุณจะต้องฉีด JSX ในข้อความโดยใช้ไวยากรณ์ <element></element> (XML) และแสดงอักขระ < และ > ในข้อความคุณจะต้องแทนที่ด้วย < (สำหรับ < ) และ > (สำหรับ > ) เอนทิตี HTML ที่ได้รับการยอมรับจากเครื่องมือการแปลเช่นนี้:
exampleApp.statsPage.targetAchieved = You achieved your weekly target (& # x3c;5) and are eligible for a <link>reward</link>.ลิงก์ Anchor เป็นลิงค์ที่นำคุณไปยังสถานที่เฉพาะในเอกสารแทนที่จะเป็นด้านบนของมัน
หนึ่งในคุณสมบัติหลักของ next-multilingual คือการรองรับ URL ที่มีการแปล การออกแบบของเราถูกสร้างขึ้นโดยใช้ประโยคปกติที่ง่ายต่อการแปลแล้วเปลี่ยนเป็นทากที่เป็นมิตรกับ SEO เราสามารถใช้ฟังก์ชั่นเดียวกันเพื่อ slugify anchor links ดังนั้นแทนที่จะมี /fr-ca/nous-joindre#our-team คุณสามารถมี /fr-ca/nous-joindre#notre-équipe
มีลิงค์ยึดสองประเภท:
หากลิงก์สมออยู่ในหน้าเดียวกันและไม่ได้อ้างอิงในหน้าอื่น ๆ คุณสามารถเพิ่มได้ในไฟล์ .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รูปแบบนี้ยังใช้ได้กับส่วนประกอบ ประโยชน์ของการทำเช่นนี้คือถ้าคุณลบหรือ refactor หน้าลิงก์สมอที่เกี่ยวข้องกับมันจะอยู่กับหน้าเสมอ
คุณสามารถสร้างองค์ประกอบข้อความที่แชร์แยกต่างหากสำหรับลิงก์สมอ แต่สิ่งนี้จะทำลายหลักการความใกล้ชิด
ตัวอย่างเต็มรูปแบบของลิงก์สมอสามารถพบได้ในแอปพลิเคชันตัวอย่าง
คุณลักษณะหนึ่งที่หายไปจาก next.js คือการจัดการแท็ก HTML ที่สำคัญที่ใช้สำหรับ SEO เราเพิ่มส่วนประกอบ <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 " /> เพื่อให้ได้ประโยชน์อย่างเต็มที่จากมาร์ก <Head> SEO ต้องรวมอยู่ในทุกหน้า มีหลายวิธีในการบรรลุเป้าหมายนี้ แต่ในตัวอย่างเราได้สร้างองค์ประกอบ <Layout> ที่ใช้ในทุกหน้า
เช่นเดียวกับเว็บไซต์ส่วนใหญ่คุณจะต้องใช้ประโยชน์จากความสามารถในการเกิดข้อผิดพลาดที่กำหนดเองของ JS 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 ที่ใช้ในแอปพลิเคชันตัวอย่าง เรากำลังใช้ส่วนหัว HTTP Accept-Language เพื่อบอก API ว่าสถานที่ที่เราต้องการตอบสนอง ซึ่งแตกต่างจากเบ็ด useMessages ที่มีบริบทของสถานที่ปัจจุบันเราต้องบอก getMessages ซึ่งสถานที่ที่จะส่งกลับข้อความ
ไฟล์ข้อความมีพฤติกรรมเช่นเดียวกับ useMessages คุณเพียงแค่สร้างไฟล์ถัดจากไฟล์เส้นทาง API ในกรณีของเรา hello.en-US.properties :
# API message
exampleApp.helloApi.message = Hello, from API.คุณสามารถนำสิ่งนี้ไปใช้ในหน้าใดก็ได้เช่นเดียวกับการโทร 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 ใช้สถานที่เป็นคำนำหน้า URL พวกเขาจะอยู่ในระดับที่ต่ำกว่าในการกำหนดค่าและสามารถทำให้เป็นปกติได้ตามต้องการ
❗เส้นทางไดนามิกมีความซับซ้อนและการแปลพวกมันเพิ่มความซับซ้อนมากขึ้น ตรวจสอบให้แน่ใจว่าคุณคุ้นเคยกับวิธีการทำงานของคุณสมบัติ next.js ก่อนที่จะพยายามเพิ่มการแปล
เส้นทางไดนามิกเป็นเรื่องธรรมดามากและรองรับนอกกรอบโดย Next.js ตั้งแต่เวอร์ชัน 3.0, next-multilingual มีการสนับสนุนเช่นเดียวกับ next.js ในแง่ของเส้นทางไดนามิก เพื่อให้เส้นทางแบบไดนามิกทำงานร่วมกับ next-multilingual เรามีรูปแบบบางอย่างที่จะติดตาม:
href ของ <Link> และ useLocalizedUrl / useGetLocalizedUrl / getLocalizedUrl อาร์กิวเมนต์ url เฉพาะยอมรับ URL สตริงเท่านั้น<Link> component which accepts a UrlObject , we preferred to streamline our types since urlObject.href can easily be used instead.userRouter().asPath (most common scenario) by providing localized parameters directly in the URL. By using asPath you are using the localized URL which means that the URL you will use will be fully localized.userRouter().pathname is conjunction with hydrateRouteParameters by providing localized parameters. By using pathname you are using the non-localized URL which means that the URL you will use might be a mix of non-localized segments plus the localized parameters. This can be useful in cases where you have nested dynamic routes.We provided several examples of on on to use dynamic routes in our dynamic route test pages.
The main challenge with dynamic routes, is that if the value of the parameter needs to be localized, we need to keep a relation between languages so that we can correctly switch languages. next-multilingual solves this problem with its getLocalizedRouteParameters API that creates a LocalizedRouteParameters object used as a page props. This can work both with getStaticProps and getServerSideProps .
getStaticProps First you need to tell Next.js which predefined paths will be valid by using getStaticPaths (all imports are added in the first example):
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 ,
}
} Then you have to pre-compute the localized route parameters and return them as props using getStaticProps and 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 } }
}If you are using a catch-all dynamic route, you will need to pass your parameters as an array, for each URL segment that you want to support. For example, if you want to support 2 levels:
const localizedRouteParameters = getLocalizedRouteParameters ( context , {
city : [ getCitiesMessages , getCitiesMessages ] ,
} ) Note that since we need to use the getMessages API instead of the useMessages hook, you will also need to export it in the message file:
export {
getMessages as getCitiesMessages ,
useMessages as useCitiesMessages ,
} from 'next-multilingual/messages'Finally you have to pass down your localized route parameters down to your language switcher component when you create your page:
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 The only part missing now is the language switcher which needs to leverage the localized route parameters by using 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 >
)
}Check out our fully working examples:
Our ideal translation process is one where you send the modified files to your localization vendor (while working in a branch), and get back the translated files, with the correct locale in the filenames. Once you get the files back you basically submit them back in your branch which means localization becomes an integral part of the development process. Basically, the idea is:
We don't have any "export/import" tool to help as at the time of writing this document.
next-multilingual ? ?Why did we put so much effort into these details? Because our hypothesis is that it can have a major impact on:
More details can be found on the implementation and design decision in the individual README files of each API and in the documentation directory.