Next.js app router 多言語対応する。

AIは嘘つきだったので、
これを参考にする。
MEMO
言語ファイル作成
上記で使用したキーを定義して行きます。
長すぎるのはダメですが、キーに日本語も使えます。
コードとメッセージが視覚的に区別できるので、解りやすいと思います。
messages/ja.json
{
"app_name": "NextAppOrigin",
"sub_title": ": ベースアプリケーション",
"env_name": {
"development": "【開発環境】",
"test": "【テスト環境】",
"staging": "【STG環境】",
"production": ""
},
"my_name": "My name",
"my_url": "https://example.com",
"サービス概要": "Next.js(React/Material UI)のベースアプリケーションです。(サービス概要に差し替え)",
"サービス説明": "サービスを迅速に立ち上げられるように、よく使う機能を予め開発しています。(サービス説明に差し替え)",
"リポジトリ": "リポジトリ",
"development": "development",
"テーマカラー確認": "テーマカラー確認",
"大切なお知らせ": "大切なお知らせ",
"アカウント登録": "アカウント登録",
"無料で始める": "無料で始める",
"ログイン": "ログイン"
}

next.config
公式の通り何も考えずに書けば良さそう。
const withNextIntl = require('next-intl/plugin')();
module.exports = withNextIntl({
// next.config.tsに記載していた元々の設定
});

i18n.ts
対応する言語だけ気をつけてたらそのままで良さそう。
import {notFound} from 'next/navigation';
import {getRequestConfig} from 'next-intl/server';
// 対応する言語一覧
const locales = ['en', 'de'];
export default getRequestConfig(async ({locale}) => {
// 現在のリクエストのロケールがサポートされていない場合は、404エラーを返します。
if (!locales.includes(locale as any)) notFound();
return {
messages: (await import(`../messages/${locale}.json`)).default
};
});

middleware.ts
import createMiddleware from "next-intl/middleware";
export default createMiddleware({
// サポートする言語の一覧
locales: ["ja", "en"],
// defaultの言語
defaultLocale: "ja",
});
export const config = {
// 国際化対応を行うpath
// ロケールを含むURLパスにマッチさせるためのパターン
matcher: ["/", "/(ja|en)/:path*"],
};

app/[locale]/layout.tsx
import { NextIntlClientProvider } from "next-intl";
import { getMessages } from "next-intl/server";
export default async function LocaleLayout({
children, // 子コンポーネント
params: { locale }, // パラメータからロケールを取得
}: {
children: React.ReactNode;
params: { locale: string };
}) {
// Providing all messages to the client
// side is the easiest way to get started
// メッセージを非同期で取得
const messages = await getMessages();
return (
// htmlタグのlang属性を動的に設定
<html lang={locale}>
<body>
{/* // メッセージ情報をクライアントに提供 */}
<NextIntlClientProvider messages={messages}>
// メッセージ情報をクライアントに提供
<div className="container mx-auto py-8 max-w-2xl">{children}</div>
</NextIntlClientProvider>
</body>
</html>
);
}
// Providing all messages to the client
// side is the easiest way to get started
このコメントの意味は、クライアントサイドで翻訳メッセージを使用するのが簡単ですよと。
国際化対応を始める際に、翻訳データをクライアントサイドに一括で渡すアプローチは、設定や管理が比較的簡単であるため、初めて国際化対応を行う場合に推奨される方法です。特に、アプリケーションがそれほど大きくない場合や、サーバーサイドレンダリングを行っている場合に有効です。と。

Next.js App Routerを使用して多言語対応を行うためのnext-intl
の設定とその流れを解説します。ここでは、公式ドキュメントを基に、ステップごとに必要な理由とともに説明します。
Next.js App Routerとは?
Next.jsのApp Routerは、従来のpages
ディレクトリに代わる新しいルーティング方式です。これを使用することで、より動的で柔軟なルーティングが可能になり、ファイルベースのルーティングと比較して、パフォーマンスの向上や開発体験の向上が期待できます。
next-intl
とは?
next-intl
は、Next.jsアプリケーションにおける国際化(i18n)を簡単に実装するためのライブラリです。多言語対応を行う際に、翻訳メッセージの管理や日時・数値のフォーマットなど、多くの便利な機能を提供します。
ステップバイステップでの設定
Step 1: 必要なパッケージのインストール
まずはnext-intl
をインストールします。
npm install next-intl
Step 2: ファイル構造の作成
多言語対応のために、以下のようなファイル構造を作成します。
├── messages
│ ├── en.json
│ └── ...
├── next.config.mjs
└── src
├── i18n.ts
├── middleware.ts
└── app
└── [locale]
├── layout.tsx
└── page.tsx
-
messages/en.json
: 各ロケールに対応するメッセージを定義します。例えば、英語では以下のようにします。{ "Index": { "title": "Hello world!" } }
Step 3: Next.jsの設定
next.config.mjs
ファイルでnext-intl
のプラグインを設定します。
// next.config.mjs
import createNextIntlPlugin from 'next-intl/plugin';
const withNextIntl = createNextIntlPlugin();
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default withNextIntl(nextConfig);
ここでcreateNextIntlPlugin
を使用する理由は、Next.jsのサーバーコンポーネントで国際化設定を利用するために、必要な設定を自動的に行うためです。
i18n.ts
Step 4: 国際化設定ファイル src/i18n.ts
では、リクエストごとにどの言語設定を使用するかを定義します。
// src/i18n.ts
import {notFound} from 'next/navigation';
import {getRequestConfig} from 'next-intl/server';
const locales = ['en', 'de'];
export default getRequestConfig(async ({locale}) => {
if (!locales.includes(locale as any)) notFound();
return {
messages: (await import(`../messages/${locale}.json`)).default
};
});
ここでgetRequestConfig
を使う理由は、リクエストごとに動的に言語設定を決定し、必要なメッセージファイルを読み込むためです。これにより、ユーザーのロケールに応じて適切なメッセージが表示されます。
middleware.ts
Step 5: ミドルウェア src/middleware.ts
で、どのロケールを使うかをURLから判断し、適切にリダイレクトやリライトを行います。
// src/middleware.ts
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
locales: ['en', 'de'],
defaultLocale: 'en'
});
export const config = {
matcher: ['/', '/(de|en)/:path*']
};
createMiddleware
を使用する理由は、リクエストが来たときにURLに基づいてロケールを自動的に判断し、その情報をリクエスト処理の流れに組み込むためです。
Step 6: レイアウトとページコンポーネント
app/[locale]/layout.tsx
各ロケールに基づいたレイアウトを定義します。
// app/[locale]/layout.tsx
import {NextIntlClientProvider} from 'next-intl';
import {getMessages} from 'next-intl/server';
export default async function LocaleLayout({
children,
params: {locale}
}) {
const messages = await getMessages();
return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
NextIntlClientProvider
を使用する理由は、クライアントサイドでの国際化設定を提供し、各コンポーネントでuseTranslations
などのフックを使えるようにするためです。
app/[locale]/page.tsx
各ページでどのようにメッセージを使用するかを示します。
// app/[locale]/page.tsx
import {useTranslations} from 'next-intl';
export default function Index() {
const t = useTranslations('Index');
return <h1>{t('title')}</h1>;
}
ここでuseTranslations
を使う理由は、定義されたメッセージから適切な翻訳を取得して表示するためです。
静的レンダリングの設定
静的レンダリングを可能にするため、generateStaticParams
とunstable_setRequestLocale
を使用します。
app/[locale]/layout.tsx
の更新
// app/[locale]/layout.tsx
import {unstable_setRequestLocale} from 'next-intl/server';
const locales = ['en', 'de'];
export function generateStaticParams() {
return locales.map((locale) => ({locale}));
}
export default async function LocaleLayout({children, params: {locale}}) {
unstable_setRequestLocale(locale);
return (
// ...
);
}
unstable_setRequestLocale
を使用する理由は、サーバーコンポーネントがリクエストのロケール情報を正確に知ることができるようにするためです。これにより、静的レンダリング時にもロケールに基づいた正しい内容をレンダリングできます。
app/[locale]/page.tsx
の更新
// app/[locale]/page.tsx
import {unstable_setRequestLocale} from 'next-intl/server';
export default function IndexPage({params: {locale}}) {
unstable_setRequestLocale(locale);
const t = useTranslations('IndexPage');
return (
// ...
);
}
まとめ
この設定により、Next.jsのApp Routerを使った多言語対応が完成します。これにより、各ページとレイアウトで動的にロケールを切り替え、適切な言語でコンテンツを提供できるようになります。next-intl
を使うことで、開発者は翻訳メッセージの管理や日時・数値の国際化などを簡単に行えるようになります。

TODO:静的レンダリング

質問: messagesディレクトリは、srcの中に入れてはいけませんか?
回答: messagesディレクトリをsrcディレクトリ内に配置することは技術的に可能ですが、公式の設定や多くのNext.jsのプロジェクトではルートディレクトリに配置することが一般的です。ただし、特定の理由がある場合(例えば、特定のビルドツールやプロジェクトの構成ポリシーに従うなど)はsrc内に配置することもできます。
ルートに配置するメリット:
Next.jsの標準的なフォルダ構成に従うことで、他の開発者がプロジェクトを理解しやすくなる。
next-intlや他の国際化ライブラリがデフォルトでルートディレクトリを参照する設定が多いため、設定がシンプルになる。
src内に配置する場合:
import文でのパスが少し長くなる可能性があります(例: import ... from '../messages/...'のようになる場合)。
プロジェクトのソースコードをsrc内に統一したい場合に選択することがあります。
実装例: messagesをsrc内に配置する場合、next.config.mjsでパスを指定するか、i18n.tsのimportパスを調整します。
// src/i18n.ts (messagesがsrc内にある場合の例)
import { notFound } from 'next/navigation';
import { getRequestConfig } from 'next-intl/server';
const locales = ['en', 'de'];
export default getRequestConfig(async ({ locale }) => {
if (!locales.includes(locale as any)) notFound();
// メッセージをsrc/messagesから読み込む
return {
messages: (await import(`../src/messages/${locale}.json`)).default
};
});

質問: 元々Topページを表示するために用意していたsrc/app/page.tsxはどうしたらいいですか?
回答: 国際化をApp Routerで実装する場合、各ロケールごとにページを用意するのが一般的です。そのため、src/app/page.tsxの内容をsrc/app/[locale]/page.tsxに移動させるのが良いでしょう。
移動先: src/app/[locale]/page.tsx
手順:
src/app/page.tsxの内容をsrc/app/[locale]/page.tsxに移動します。
移動後、src/app/page.tsxは削除します。
ロケールデータの読み込み: 国際化対応のために、ページ内でuseTranslationsを使用して適切な翻訳を表示するようにします。
移動と更新後の
src/app/[locale]/page.tsx
以下は、src/app/page.tsxの内容をsrc/app/[locale]/page.tsxに移動させ、国際化対応を行う例です。
import { useTranslations } from 'next-intl';
import FAQComponent from "@/features/home/components/FAQ";
import { FAQData } from "@/features/home/types/faq";
import { getFAQData } from "@/lib/faq";
import CTA from "@/features/home/components/CTA";
import Title from "@/features/home/components/Title";
import Feature from "@/features/home/components/Feature";
import Copy from "@/features/home/components/Copy";
import ShareButtons from "@/components/elements/ShareButtons/ShareButtons";
export default async function Home() {
const t = useTranslations('Home'); // Home名前空間で翻訳を取得
const faqData: FAQData = await getFAQData();
return (
<main className="flex min-h-screen flex-col items-center justify-between">
<div className="w-full">
<section className="hero bg-muted py-16">
<Title />
<div className="mt-16">
<CTA />
</div>
<div className="mt-16 flex items-center justify-center flex-col">
<p className="mr-4 mb-4 text-sm">{t('share')}</p> // 翻訳を使用
<ShareButtons />
</div>
</section>
<section className="feature py-16">
<Feature />
</section>
<div className="py-4">
<CTA />
</div>
<section className="copy py-16">
<Copy />
</section>
<section className="faq">
<FAQComponent faqs={faqData} />
</section>
</div>
</main>
);
}

http://localhost:3000/が404 not foundになっちゃった

エグザンプルがあったので参考にする。
[locale]に移したsrc/app/page.tsxの代わりに、src/app/page.tsxをあたらしく作る。
import {redirect} from 'next/navigation';
// This page only renders when the app is built statically (output: 'export')
export default function RootPage() {
redirect('/en');
}

src/config.ts
import {Pathnames} from 'next-intl/navigation';
export const port = process.env.PORT || 3000;
export const host = process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: `http://localhost:${port}`;
export const defaultLocale = 'en' as const;
export const locales = ['en', 'de'] as const;
export const pathnames = {
'/': '/',
'/pathnames': {
en: '/pathnames',
de: '/pfadnamen'
}
} satisfies Pathnames<typeof locales>;
// Use the default: `always`
export const localePrefix = undefined;
export type AppPathnames = keyof typeof pathnames;

import { Pathnames } from "next-intl/navigation";
export const port = process.env.PORT || 3000;
export const host = process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: `http://localhost:${port}`;
export const locales = ["en", "ja"] as const;
export const pathnames = {
"/": "/",
// "/pathnames": {
// en: "/pathnames",
// ja: "/pfadnamen",
// },
} satisfies Pathnames<typeof locales>;
// Use the default: `always`
export const localePrefix = "always"; // https://next-intl-docs.vercel.app/docs/routing/middleware#locale-prefix
export type AppPathnames = keyof typeof pathnames;
// "/pathnames": {
// en: "/pathnames",
// ja: "/pfadnamen",
この意味は、パスの名前を多言語かするか?ということ。
EX)/en/about
/de/ueber-uns
今回は必要ないのでしません。

Unable to find next-intl
locale because the middleware didn't run on this request. See https://next-intl-docs.vercel.app/docs/routing/middleware#unable-to-find-locale. The notFound()
function will be called as a result.
こんなエラーが出た。
ミドルウェアが間違ったファイルにセットアップされています (例:srcフォルダーを使用していますが、middleware.tsルート フォルダーに追加されているなど)。
ルートにあった。。。

srcに直したら、こんなエラーが出た。
⨯ Internal error: Error: Expected a suspended thenable. This is a bug in React. Please file an issue.
⨯ Error: failed to pipe response
ここが参考になりそう。
これ見ろって言ってた。
getTranslationsuseTranslations は非同期のサーバー コンポーネントでは機能しません。代わりにfrom を使用する必要があるかもしれませんnext-intl/server。このリンクをご覧くださいhttps://next-intl-docs.vercel.app/docs/environments/server-client-components#async-components
確かに。多言語対応の部分をコメントアウトしたらエラー出なくなった。
該当コード
import ShareButtons from "@/components/elements/ShareButtons/ShareButtons";
import Copy from "@/features/home/components/Copy";
import CTA from "@/features/home/components/CTA";
import FAQComponent from "@/features/home/components/FAQ";
import Feature from "@/features/home/components/Feature";
import Title from "@/features/home/components/Title";
import { FAQData } from "@/features/home/types/faq";
import { getFAQData } from "@/lib/faq";
// import { useTranslations } from "next-intl";
export default async function Home() {
// const t = useTranslations("Home"); // Home名前空間で翻訳を取得
const faqData: FAQData = await getFAQData();
return (
<main className="flex min-h-screen flex-col items-center justify-between">
<div className="w-full">
<section className="hero bg-muted py-16">
<Title />
<div className="mt-16">
<CTA />
</div>
<div className="mt-16 flex items-center justify-center flex-col">
{/* <p className="mr-4 mb-4 text-sm">{t("share")}</p> */}
<ShareButtons />
</div>
</section>
<section className="feature py-16">
<Feature />
</section>
<div className="py-4">
<CTA />
</div>
<section className="copy py-16">
<Copy />
</section>
<section className="faq">
<FAQComponent faqs={faqData} />
</section>
</div>
</main>
);
}
だし、確かに非同期処理してた...!!
const faqData: FAQData = await getFAQData();

というか、このfaqの部分、非同期処理要らなくね?という気づき。
FAQのデータを取得するために非同期処理を使用しているようですが、この場合、非同期処理を使用する必要はありません。
getFAQData
関数では、fs.promises.readFile
を使用して非同期的にファイルを読み込んでいますが、これをブロッキングなfs.readFileSync
に変更することで、同期的にファイルを読み込むことができます。
以下のようにgetFAQData
関数を変更してください:
// src/lib/faq.ts
import fs from "fs";
import path from "path";
import { FAQData } from "@/features/home/types/faq";
export function getFAQData(): FAQData {
const filePath = path.join(
process.cwd(),
"src",
"features/home/data/faq.json"
);
const jsonData = fs.readFileSync(filePath, "utf8");
const data: FAQData = JSON.parse(jsonData);
return data;
}
そして、Home
コンポーネントではgetFAQData
関数を同期的に呼び出すように変更します:
// ...
export default function Home() {
const faqData: FAQData = getFAQData();
return (
// ...
);
}
これにより、非同期処理を使用せずにFAQデータを取得することができます。
ただし、注意点として、fs.readFileSync
はブロッキングな操作であるため、大きなファイルを読み込む場合にはパフォーマンスに影響を与える可能性があります。FAQのデータが比較的小さい場合は問題ありませんが、大量のデータを扱う場合は非同期処理を検討する必要があります。
また、Next.jsのgetStaticProps
やgetServerSideProps
を使用する場合は、非同期処理を使用することが推奨されています。これらの関数内では、非同期処理を使用してデータを取得し、propsとしてコンポーネントに渡すことができます。
今回のケースでは、FAQのデータが静的なものであれば、getStaticProps
を使用してビルド時にデータを取得するのが良いでしょう。
以上の点を考慮して、FAQデータの取得方法を適切に選択してください。

直したのにまだエラー出る。なんで、と思ってたら。
export default async function Home() {
こうなってた。
export default function Home() {
こう直したら無事通った。

言語切り替えselect実装
- navigation.tsの作成
- スイッチ用のコンポーネント作成
- src/components/elements/LocaleSwitch/LocaleSwitcher.tsx
-
src/components/elements/LocaleSwitch/LocaleSwitcherSelect.tsx
エグザンプルはプルダウン式の選択だったが、
JA / EN のようにしたかったので、修正。
src/components/elements/LocaleSwitch/LocaleSwitcher.tsx
"use client";
import { locales } from "@/config";
import { usePathname, useRouter } from "@/navigation";
import { useLocale } from "next-intl";
import { useTransition } from "react";
export default function LocaleSwitcher() {
const router = useRouter();
const [isPending, startTransition] = useTransition();
const pathname = usePathname();
const currentLocale = useLocale();
function onLocaleChange(nextLocale: string) {
startTransition(() => {
router.replace({ pathname }, { locale: nextLocale });
});
}
return (
<div className="flex space-x-4">
{locales.map((locale) => (
<button
key={locale}
className={`${
currentLocale === locale
? "text-blue-500 font-bold"
: "text-gray-400"
} ${isPending ? "opacity-50 cursor-not-allowed" : ""}`}
onClick={() => onLocaleChange(locale)}
disabled={isPending}
>
{locale === "ja" ? "JA" : "EN"}
</button>
))}
</div>
);
}

現在、http://localhost:3000/en/restrictionsこれは表示できるのですが、 日本語の時にはhttp://localhost:3000/restrictionsこれで表示できるようにしたいのですが、どうしたらいいですか
-> なんかよくわからなかったので、
export const localePrefix = "always";
にして、プレフィックスをぜったいつけるようにしちゃう。

これも参考になりそう

サイト内リンクをプレフィックスに対応させる。
+ import { createSharedPathnamesNavigation } from "next-intl/navigation";
export const locales = ["en-us", "ar-eg"] as const;
export type Locale = (typeof locales)[number];
+ export const { Link } = createSharedPathnamesNavigation({ locales });
import { useTranslations } from "next-intl";
- import Link from "next/link";
+ import { Link } from "@/i18n.config";
export default function Header() {
const t = useTranslations("Header");
// ...
}

/config.ts
で管理してたけど、よくわからなくなるので、
/i18n.config.ts
に変更する。

{
"Home": {
"このページをシェアする": "do share"
}
}
{
"Home": {
"このページをシェアする": "このページをシェアする"
}
}
<p className="mr-4 mb-4 text-sm">{t("このページをシェアする")}</p>
これでいけた...!!!すげー。わかりやすい〜

マークダウンで管理していた部分も多言語対応させる。
1.言語ごとのマークダウンファイルを用意する
* privacy.en.md: 英語版の規約ページ用のマークダウンファイル
* privacy.ja.md: 日本語版の規約ページ用のマークダウンファイル
2. next-intl を使用して、言語ごとのマークダウンファイルを動的にインポートする
import { useLocale } from "next-intl";
import MarkdownContent from "@/features/terms/components/MarkdownContent";
export default async function PrivacyPage() {
const locale = useLocale();
const privacyContent = await import(`@/features/terms/data/privacy.${locale}.md`).then(
(module) => module.default
);
return <MarkdownContent content={privacyContent} />;
}

辞書の中で改行を扱いたい!
これを参照。
import PreRegisterButton from "@/components/elements/Button/PreRegisterButton";
import { useTranslations } from "next-intl";
export default function CTA() {
const t = useTranslations("CTA");
return (
<div className="flex flex-col items-center">
<PreRegisterButton />
<p className="text-center text-xs text-gray-500 pt-2">
{t.rich("CTA", { br: () => <br /> })}
</p>
</div>
);
}
{
"TOP": {
"このページをシェアする": "do share"
},
"CTA": {
"CTA": "betadsad<br></br>dasdas<br></br>adassdad."
}
}
これでうまくいった。
※改行タグを、
の省略形だとうまくいかないので、<br></br>で書く。
ここで何をしているかの考察
brというタグがあったら、改行タグに変換するぞ。をしている。

辞書の中でクラスを当てたい
先ほどのページを参照。
brというタグがあったら、改行タグに変換するぞ。をしている。
ように、
XXXXというタグがあったら、YYYYタグに変換するという書き方をしたらいけるはず。
一つのワードに複数のタグを入れたい
rich関数の中身。
(method) rich<string>(key: string, values?: RichTranslationValues | undefined, formats?: Partial<Formats> | undefined): string | ReactElement<any, string | JSXElementConstructor<...>> | ReactNodeArray
以下のようにしたらいけるらしい →いけた。
{t.rich("特徴の説明文", {
br: () => <br />,
strong: (text) => <span className="bg-yellow-200">{text}</span>,
})}
スマホ版で改行させたいとき。これも便利。
{t.rich("特徴の説明文", {
br: () => <br />,
brSp: () => <br className="md:hidden" />,
})}
"XXXXX": "ssss<brSp></brSP> ssss<br></br>sss",
※<brSp></brSP>の前か後ろにスペース入れないと、PCの時にスペースがつかない

辞書を階層構造にしたい。
{
"auth": {
"SignUp": {
"title": "Sign up",
"form": {
"placeholder": "Please enter your name",
"submit": "Submit"
}
}
}
}
import {useTranslations} from 'next-intl';
function SignUp() {
// Provide the lowest common denominator that contains
// all messages this component needs to consume.
const t = useTranslations('auth.SignUp');
return (
<>
<h1>{t('title')}</h1>
<form>
<input
// The remaining hierarchy can be resolved by
// using `.` to access nested messages.
placeholder={t('form.placeholder')}
/>
<button type="submit">{t('form.submit')}</button>
</form>
</>
);
}

言語によって参照するファイルを変える
マークアップファイル
import MarkdownContent from "@/features/terms/components/MarkdownContent";
import { useLocale } from "next-intl";
export default async function PrivacyPage() {
const locale = useLocale();
const privacyContent = await import(
`@/features/terms/data/privacy.${locale}.md`
).then((module) => module.default);
return <MarkdownContent content={privacyContent} />;
}
jsonファイル
import { FAQData } from "@/features/home/types/faq";
import fs from "fs";
import { useLocale } from "next-intl";
import path from "path";
export function getFAQData(): FAQData {
const locale = useLocale();
const filePath = path.join(
process.cwd(),
"src",
`features/home/data/faq.${locale}.json`
);
const jsonData = fs.readFileSync(filePath, "utf8");
const data: FAQData = JSON.parse(jsonData);
return data;
}

言語によって画像を出し分けたい
import imgPL from '/public/images/myImage_pl.png';
import imgEN from '/public/images/myImage_en.png';
import { useLocale } from 'next-intl';
export default function Page() {
const locale = useLocale();
return (
<Image src={locale==='pl' ? imgPL:imgEN} alt="" width={200} />
);
}
import { useLocale } from 'next-intl';
export default function Page() {
const locale = useLocale();
return (
<Image src={`/images/myImage_${locale}.png` alt="" width={200} />
);
}