フォーカスと優先順位の変化により、このパッケージを維持することはできません。更新、バグの修正、または新機能を受け取らず、時間とともに互換性がない場合があります。最新のnext.jsアプリディレクトリをサポートするパッケージに切り替えることをお勧めします。
next-multilingual next.jsアプリケーションのための意見のあるエンドツーエンドのソリューションです。複数の言語が必要です。
デモアプリをご覧ください!
✱ next-multilingual一緒に機能しますか? pagesディレクトリ。私たちはまだ新しいものをサポートするためのソリューションをアイロンをかけていますか? app Directory Internationalizationはnext.js configの一部ではなくなりました。
npm install next-multilingual
useMessagesフック。/en-us/contact-usには、カナダのフランス語に/fr-ca/nous-joindre )を使用します。 defaultデフォルトのロケールスラッグは、 pagesディレクトリファイルシステムと一致する必要があります(たとえば、「About Us」のスラッグは、 about-usディレクトリにある必要があります)。必要なデフォルトのロケールがファイルシステムでサポートされている文字を超えた文字を使用している場合、テストされていないため、機能しない可能性があります。プルリクエストは大歓迎ですか?
next-multilingualすべてのAPIにTSDOCを追加することに多くの努力を払っています。例で提供されている特定のAPIを使用する方法がわからない場合は、IDEを直接確認してください。
また、「ベストプラクティス」について意見を述べるのは簡単なことではありません。これが、ここで参照できる特別な文書で設計上の決定を文書化した理由です。一部のAPIが期待するものを提供していないと感じた場合は、問題を開く前にこのドキュメントを必ず参照してください。
アクションにすぐに飛び込むことを好む人のために、 next-multilingualのエンドツーエンドの実装については、 exampleディレクトリを見てください。残りについては、以下のセクションでは、完全な段階的な構成ガイドを提供します。
目標を達成するために、next.jsで構成する多くのオプションがあります。 next-multilingualは主に気にします:
このステップを簡素化するために2つのAPIを提供します。
getConfig (Simple Config)この関数は、ほとんどのユースケースを満たすnext.js構成を生成します。 getConfig次の議論を取ります。
applicationId - メッセージキープレフィックスとして使用される一意のアプリケーション識別子。locales - アプリケーションのロケール。defaultLocaleアプリケーションのデフォルトのロケール( localesにも含める必要があります)bcp
languageに続くBCP 47言語タグのみ -country形式が受け入れられます。理由の詳細については、設計決定文書を参照してください。
options (オプション) - 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によってサポートされていないわけではありません。たまたま使用した場合、エラーメッセージは次のセクションであるAdvanced Configを直接指します。
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次の2つのことを実行します。JSの現在のルーティング機能:
next-multilingual/config 、 Linkコンポーネントのnext-multilingual/link/ssrを使用して、ローカライズされたURLのサーバーサイドレンダリングに必要な特別なWebパック構成を処理し、 Headコンポーネントの標準的および代替リンク用のnext-multilingual/head/ssr SSR/SSRも処理します。
UTF-8文字を使用している理由などの実装の詳細については、Design Decisions Documentsを参照してください。
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これは基本的に、コメントで述べたように、2つのことを行います。
/ )なしでホームページを押すときに再利用できるようにします。 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>タグに正しいサーバー側のロケールを表示します。 「偽の」デフォルトのロケールを使用しているため、特に/ onで動的なロケールを解決する場合は、正しい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 、完全に資格のあるURLを生成するために、 NEXT_PUBLIC_ORIGINという変数を探します。 next.js ' basePathを使用している場合、ベースURLに自動的に追加されます。
NEXT_PUBLIC_ORIGIN 、パスなしで完全に適格なドメイン( http://example.comなど)のみを受け入れます。
next-multilingualを使用していますか?すべてが構成されたので、 next-multilingual使用に焦点を当てることができます!
next-multilingualを設計どおりに機能させるために、2つの問題の解決策を見つけなければなりませんでした。
undefined値:next.jsはロケールのないサイトをサポートするため、ネイティブタイプはundefined値を持つことができます。これは、私たちの場合は迷惑であり、追加のキャストが必要です。next-multilingual使用しないデフォルトのロケールを作成する必要があります。これは、関連するロケール情報にアクセスするために、next.jsのAPIに依存することはできないことを意味します。次のAPIを作成して、アプリケーション全体で一貫したロケール値を許可します。
useRouterこれは、next.js ' 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 : { } }
}
ショ和 スマートロケール検出を使用してホームページを動的にレンダリングすることをお勧めしますが、これは完全にオプションです。localeDetection: true、getServerSidePropsを使用する必要なく、デフォルトのnext.jsの動作を復元します。
ホームページは、次の理由で動的ロケール検出(および表示)を実装する必要があるため、他のページよりも少し複雑です。
/をリダイレクトすることは、SEOにマイナスの影響を与える可能性があり、最高のユーザーエクスペリエンスではありません。next-multilingualデフォルトのnext.js実装よりもスマートな自動検出を提供するgetPreferredLocale APIが付属しています。この例では完全な実装を見つけることができますが、ここに剥がれたバージョンがあります。
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ロケールCookieで利用できるかどうかを確認します。useResolvedLocaleを使用してルーターの値を上書きし、アプリケーション全体でこの動的にします。tsx 、 ts 、 jsx 、またはjs (Compilable)ファイルを作成し、ローカライズされたメッセージが必要な場合は、これらのファイルによってのみ使用可能なサポートされているロケールにメッセージファイルを作成するだけです。 CSSモジュールと同様に、アイデアは、別のファイルのローカルスコープに関連付けられたメッセージファイルを持つことができるということです。これには、メッセージをよりモジュール化するという利点があり、また、さまざまなコンテキストでメッセージを共有することも避けます(詳細については、これが悪い理由に関するデザイン決定文書の詳細)。
メッセージファイルには2つの主要なユースケースがあります。
pagesディレクトリ内のページの場合、 slugキー識別子を使用して、ローカライズされたURLセグメント(パスの終わりの間/またはパスの間にURLの一部)を指定できます。これを以下に行う方法の詳細。useMessagesフックを使用して、ローカルスコープのみで利用できます。 CSSを想像しますが、ローカライズ可能な文字列用。要約するには:
これらのファイルの作成と管理は、スタイルシートを作成するのと同じくらい簡単ですが、重要な詳細は次のとおりです。
.propertiesファイルです。はい、なぜ疑問に思うかもしれませんが、設計決定文書に文書化された正当な理由があります。UTF-8に設定されていることを確認してください。そうしないと、非ラチン文字を�に置き換えます �.propertiesファイルの組み込みIDEサポートの一部を活用するために、厳格な命名規則に従います: <PageFilename>.<locale>.properties<applicationId>.<context>.<id>厳格な命名規則に従う一意のキーが必要です。next-multilingual/configで設定されたものと同じ値を使用する必要がありますaboutUsPageやfooterComponentについては、コンテキストの良い例です。各ファイルには1つのコンテキストを含めることができ、コンテキストは多くのファイルで使用しないでください。.また、1〜50個の英数字のみを含めることができます - 読みやすさのためにキャメルケースを使用することをお勧めします。slug識別子にキーを含むメッセージファイルを含める必要があります。title識別子にキーを含めてください。next-multilingual/messagesで提供されているgetTitle APIを使用して、 titleとslugキーの間の自動的にフォールバックします。useMessagesフックを使用する場合にのみファイルが必要です。また、メッセージの潜在的な問題に関する警告については、コンソールログを確認してください。最初にどのように機能するかに慣れるのは難しい場合がありますが、問題を簡単に検出して修正しようとしました。これらのログは、非生産環境でのみ表示されることに注意してください。
フックを使用できない間にローカライズされたメッセージが必要であることは珍しくありません。例は、next.jsのコア機能の1つを使用しているときに、APIサポートの組み込みです。その文脈では、 useMessage使用する代わりに、 locale引数を指定しながらgetMessages使用するだけです。
前述のように、 idがslugであるpagesには1つの特別なキーがあります。 this-is-a-pageように見える伝統的なナメクジとは異なり、他の文字列と同じように翻訳できるように、通常の人間の読み取り可能な文としてナメクジを書くように頼みます。これにより、複数の言語で管理するために費用がかかり複雑なスラグ用の特別なプロセスがありません。
基本的に、 slugあなたのページの人間の読み取り可能な「短い説明」であり、URLのセグメント(パスの終わりの間/または間の一部)を表します。 URLセグメントとして使用すると、次の変換が適用されます。
-たとえば、 About Us about-usなります。
ホームページの場合、URLは常に存在します/これは、 slugキーを使用してローカライズされたURLセグメントを作成しないことを意味します。
忘れないでください、ナメクジは通常の短い説明として書かれている必要があります。つまり、SEOのためにそれを短くするために言葉をスキップすることは落胆することを意味します。これの主な理由は、「キーワードの束」を書くと、SEOに慣れていない言語学者がそのメッセージを翻訳するのに苦労するかもしれないということです。多くの言語にSEOスペシャリストがいることも、非常に費用がかかり、拡張が困難です。理想的なシナリオでは、市場固有のSEOページはおそらく母国語で執筆および最適化する必要がありますが、これはもはや翻訳プロセスの一部ではありません。 next-multilingualの焦点は、多くの言語でURLをローカライズするための簡単で合理化されたソリューションを提供することです。
slugキーはnext-multilingual/messagesで提供されているgetTitle APIを使用する場合、 titleキーのフォールバックとしても使用されます。このAPIにより、スラグが不十分だと感じたときにタイトルを簡単にカスタマイズできます。
ショ和 slug値を変更すると、URLが変更されることに注意してください。これらの変更はnext.config.jsで発生しているため、next.js構成の変更と同様に、有効な変更を確認するためにサーバーを再起動する必要があります。基礎となる構成はこれに依存しているため、フォルダー構造を変更すると同じことが当てはまります。
ページのないディレクトリを使用したい場合は、インデックスを作成して、インデックスを作成localeてローカライズできますindex.<locale>.propertiesこのオプションはサポートされていますが、SEOベストプラクティスに反するURLパスが長くなるため、使用することはお勧めしません。
デフォルトでは、 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局所的なURLのクライアント側とサーバーサイドのレンダリングを可能にする独自の<Link>コンポーネントが付属しています。使用法は簡単で、next.js <Link>とまったく同じように機能します。
覚えておくべき唯一の重要なことは、 href属性には常にnext.js urlを含める必要があるということです。つまり、 pagesフォルダーの下のファイル構造は、ローカライズされたバージョンではなく、使用されるものである必要があります。
言い換えれば、ファイル構造は「非局在化された」URL表現と見なされ、 <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パスは/fr-ca/nous-joindre /en-us/contact-us /contact-usです。
このマッピングのデータはレンダリング中にすぐには利用できないため、 next-multilingual/link/ssrサーバーサイドレンダリング(SSR)を処理します。 next-multilingual/configのgetConfig使用することにより、WebPack構成が自動的に追加されます。 Advanced Config Methodを使用している場合、これは以前の例で特別なWebpack構成が必要な理由を説明します。
すべてのローカライズされたURLが<Link>コンポーネントを使用しているわけではなく、これがnext.jsに他の多くのユースケースで使用できるrouter.pushメソッドを持っている理由でもあります。 next-multilingualこれらのユースケースをサポートできます。これにより、コンポーネントが使用できるローカライズさ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操作を行う必要がある場合は、機能を返すようにURLSを取得するuseGetLocalizedUrlフックを使用することもできます。
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 TestsReact要素内のURLの文字列値を使用する場合は、URLがプレレンダリングとブラウザの間で異なるため、エラーが発生します。この理由は、クライアントでは、最初のレンダリングで、次のレンダリングでは、JSがデータにアクセスできないため、「半ローカライズされた」URLパス(EG
/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また、すべての多言語アプリケーションで必須の言語スイッチャーコンポーネントの例を必ず確認してください。
メッセージを共有することは最初から悪い習慣であることが明らかになったので、ここで何について話しているのでしょうか?実際、メッセージを単独で共有することは悪くありません。問題を引き起こす可能性があるのは、異なるコンテキストでメッセージを共有するときです。たとえば、 Button.ts yesButton 、 noButton keysを含む共有メッセージファイルを作成するように誘惑されるかもしれませんが、これは間違っているでしょう。多くの言語では、「はい」や「いいえ」などの単純な単語では、ボタンであっても、コンテキストに応じて異なるスペルを持つことができます。
メッセージを共有するのはいつですか?アイテムのリスト用。
たとえば、ローカリゼーションプロセスをシンプルに保つには、データベースにローカライズ可能な文字列の保存を避けたいと思います(Design Decision Documentの詳細の詳細)。データベースでは、一意の識別子を使用してコンテキストを識別し、キーの識別子がデータベースの識別子と一致する共有メッセージファイルにメッセージを保存します。
これを説明するために、果物を使用して1つの例を作成しました。あなたがする必要があるのは、このようなuseMessages呼び出すフックを作成することだけです。
export { useMessages as useFruitsMessages } from 'next-multilingual/messages'fookフック以外でメッセージにアクセスする必要がある場合は、
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 ICU MessageFormat構文をボックスからサポートしています。つまり、次のメッセージを使用できます。
exampleApp.homepage.welcome = Hello {name}!次の値を使用して値を挿入します。
messages . format ( 'welcome' , { name : 'John Doe' } ) formatの使用方法formatを使用する際に留意すべきいくつかの簡単なルールがあります。
values引数を提供しない場合、メッセージを静的テキストとして単純に出力します。values引数を提供する場合、メッセージに{placeholder}構文を使用してすべてのプレースホルダーの値を含める必要があります。それ以外の場合、メッセージは表示されません。valuesを提供する場合、それらは静かに無視されます。 ICU MessageFormatの主な利点の1つは、Unicodeのツールと標準を使用して、アプリケーションがほとんどの言語で流fluentに聞こえるようにすることです。多くのエンジニアは、2つのメッセージを持っていることで、1つは特異なものと複数のメッセージを1つだけで十分に言及していると信じるかもしれません。実際、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}構文を使用して両方のプレースホルダーを使用し、メッセージに{および}文字を表示する必要があるまれな場合、 {に置き換える必要があります。 ( { )および} ( } )このような翻訳ツールによって認識されるHTMLエンティティ:
exampleApp.debuggingPage.variableInfo = Your variable contains the following values: & # x7b;{values}}値(プレースホルダー)のないメッセージがある場合、HTMLエンティティを使用して{および}を脱出することは必須ではなく、エンティティを静的テキストとして表示します。
単一のメッセージ内にインラインHTMLを持つ必要があるのは非常に一般的な状況です。これを行う1つの方法は次のとおりです。
# 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としてサポートしています。これは、JSX要素を注入しながら標準format機能(複数形など)から利益を得ることができることを意味します。
formatを使用する際に留意すべきいくつかの簡単なルールがあります。
formatJsxへの引数として渡すJSX要素にある必要があります。<link> xmlタグの場合、 link: <Link href="/"></Link>を使用してJSX要素を提供する必要があります。<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です。 > < formatJsxを使用する場合、テキストとして表示する場合は、巻き毛ブラケットを逃れる必要があります。さらに、 formatJsxメッセージでXMLを使用するため、タグを識別するために使用される同様>ルールが<に適用されます。
<element></element> (xml)構文を使用してJSXをメッセージに挿入する必要がある場合、メッセージに< >を表示する必要がある場合、 <に置き換える必要があります。 ( < )および> ( > )このような翻訳ツールによって認識されるHTMLエンティティ:
exampleApp.statsPage.targetAchieved = You achieved your weekly target (& # x3c;5) and are eligible for a <link>reward</link>.アンカーリンクは、上部ではなくドキュメント内の特定の場所に移動するリンクです。
next-multilingualのコア機能の1つは、ローカライズされたURLをサポートすることです。私たちのデザインは、ローカライズが簡単な通常の文を使用して構築され、その後SEOに優しいナメクジに変換されています。同じ関数を使用してアンカーリンクをスリギーすることができます。これにより、 /fr-ca/nous-joindre#our-teamている代わりに/fr-ca/nous-joindre#notre-équipe持つことができます。
アンカーリンクには2つのタイプがあります。
アンカーリンクが同じページにあり、他のページに参照されていない場合は、次のようなページに関連する.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.jsから欠落している機能の1つは、SEOに使用される重要なHTMLタグを管理することです。 <Head>コンポーネントを追加して、HTML <head>に住んでいる2つの非常に重要なタグを扱う:
<link rel=canonical> ):これは、検索エンジンに、閲覧されるページの真実の原因がこのURLであることを伝えます。特にURLは症例の鈍感であるため、重複するコンテンツに対して罰せられることを避けるために非常に重要ですが、Googleはそれらを症例に敏感であると扱っています。<link rel=alternate> ):これは、閲覧されているページが他の言語でも利用できることを検索エンジンに伝え、サイトのrawりを促進します。 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はしばしばローカライズする必要があります。これが「ハロー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に伝える必要があります。
メッセージファイルは、APIルートのファイルの横にあるものを作成するために必要なuseMessages 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はロケールをURLプレフィックスとして使用するため、構成が低く、必要に応じて再正規化できます。
❗動的ルートは複雑で、それらをローカライズするとさらに複雑さが増します。ローカリゼーションを追加しようとする前に、このnext.js機能がどのように機能するかに精通していることを確認してください。
動的ルートは非常に一般的であり、next.jsによって箱から出してサポートされています。バージョン3.0以来、 next-multilingual動的ルートの観点からNext.jsと同じサポートを提供します。 next-multilingualで動的ルートを機能させるには、次のパターンがいくつかあります。
<Link>のhref属性と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.