Babelize Elements
言語スイッチャー

言語スイッチャー

ドロップダウン、検索、RTL サポート、完全なアクセシビリティを備えた、コピー&ペーストで使える React 向けの言語スイッチャーです。next-intl、react-i18next、その他の i18n 設定で動作します。

インストール

npx shadcn@latest add @babelize/language-switcher

使い方

"use client";

import { useState } from "react";
import { LanguageSwitcher } from "@/components/ui/language-switcher";

const locales = [{ code: "en" }, { code: "fr" }, { code: "es" }, { code: "ja" }, { code: "ar" }];

export function LocaleSwitcher() {
  const [locale, setLocale] = useState("en");

  return (
    <LanguageSwitcher
      locales={locales} // only `code` is required, labels and flags are auto-resolved
      value={locale} // omit for uncontrolled, use defaultValue instead
      onValueChange={setLocale}
      label="native" // or "english" (default)
      showFlags
    />
  );
}

Props

Prop型必須説明
localesLocale[]はい利用可能なロケールの配列です。必須なのは code のみです
valuestringいいえ選択中のロケールコード。コンポーネントを制御するにはこの値を渡します
defaultValuestringいいえ初期選択ロケールコード(デフォルト: 最初のロケールのコード)
onValueChange(code: string) => voidいいえロケールが変更されたときのコールバック
label"native" | "english"いいえ表示ラベルをネイティブ言語または英語で表示します(デフォルト: "english")
showFlagsbooleanいいえ国旗の絵文字を表示します(デフォルト: false)
maxHeightnumber | stringいいえスクロールする選択肢一覧の最大の高さ(デフォルト: "15rem")
showCodebooleanいいえ各選択肢の横にロケールコードを表示します(デフォルト: false)
variant"outline" | "ghost" | "secondary"いいえトリガーのスタイル(デフォルト: "outline")
size"sm" | "default" | "lg"いいえトリガーのサイズ(デフォルト: "default")
align"start" | "end"いいえメニューを揃えるトリガーの辺(デフォルト: "start")
side"top" | "bottom"いいえ優先するメニューの表示側。スペースがない場合は反転します(デフォルト: "bottom")
searchablebooleanいいえ検索欄を表示します(デフォルト: true)
searchPlaceholderstringいいえ検索欄のプレースホルダー(デフォルト: "Search languages...")
emptyMessageReactNodeいいえ検索に一致する項目がない場合に表示されます(デフォルト: "No languages found.")
labelsPartial<LanguageSwitcherLabels>いいえ組み込みの英語 UI テキストを上書きします(下記参照)
disabledbooleanいいえトリガーを無効にします
renderOption(locale, state) => ReactNodeいいえ各選択肢の行の内容を置き換えます。state には selected、active、label、flag があります
classNamesLanguageSwitcherClassNamesいいえパーツごとのクラス: trigger、content、search、list、item、empty
classNamestringいいえルートに追加する CSS クラス

Locale オブジェクト

プロパティ型説明
codestringISO 639-1 / BCP 47 コード(例: "en"、"fr-FR"、"ar-SA")— 必須
labelstring表示名を上書きします(省略時は組み込みのマッピングから自動で解決されます)
flagstring国旗の絵文字を上書きします(省略時はコードから自動検出されます)
rtlbooleanRTL を上書きします(省略時はコードから自動検出されます)

使用例

Babelize SDK を使う場合

自動翻訳のために、言語スイッチャーを Babelize SDK と組み合わせます:

"use client";

import { useState } from "react";
import { useRouter } from "next/navigation";
import { LanguageSwitcher } from "@/components/ui/language-switcher";
import { useBabelize } from "@babelize/sdk/react";

const locales = [
  { code: "en", label: "English", flag: "🇺🇸" },
  { code: "fr", label: "Français", flag: "🇫🇷" },
  { code: "es", label: "Español", flag: "🇪🇸" },
  { code: "de", label: "Deutsch", flag: "🇩🇪" },
  { code: "ja", label: "日本語", flag: "🇯🇵" },
  { code: "ar", label: "العربية", flag: "🇸🇦" },
];

export function LocaleSwitcher() {
  const router = useRouter();
  const { locale, setLocale } = useBabelize();

  return (
    <LanguageSwitcher
      locales={locales}
      value={locale}
      onValueChange={(code) => {
        setLocale(code);
        router.push(`/${code}`);
      }}
    />
  );
}

next-intl を使う場合

"use client";

import { useRouter } from "next/navigation";
import { LanguageSwitcher } from "@/components/ui/language-switcher";

export function LocaleSwitcher() {
  const router = useRouter();

  return (
    <LanguageSwitcher
      locales={[{ code: "en" }, { code: "fr" }]}
      defaultValue="en"
      onValueChange={(code) => router.push(`/${code}`)}
    />
  );
}

react-i18next を使う場合

"use client";

import { useTranslation } from "react-i18next";
import { LanguageSwitcher } from "@/components/ui/language-switcher";

export function LocaleSwitcher() {
  const { i18n } = useTranslation();

  return (
    <LanguageSwitcher
      locales={[{ code: "en" }, { code: "fr" }]}
      value={i18n.language}
      onValueChange={(code) => i18n.changeLanguage(code)}
    />
  );
}

ネイティブラベル+国旗

"use client";

import { LanguageSwitcher } from "@/components/ui/language-switcher";

export default function App() {
  return (
    <LanguageSwitcher
      locales={[{ code: "en" }, { code: "fr" }, { code: "es" }]}
      defaultValue="en"
      label="native"
      showFlags
    />
  );
}

パーツのカスタマイズ

このスイッチャーは shadcn のテーマトークンでスタイル設定されているため、お使いのテーマに追従します。 任意のパーツのスタイルを変更するには classNames を、各行に表示する内容を変更するには renderOption を使用します:

<LanguageSwitcher
  locales={locales}
  variant="ghost"
  size="sm"
  align="end"
  classNames={{ content: "w-64", item: "py-2" }}
  renderOption={(locale, { label, flag, selected }) => (
    <>
      <span className="text-base leading-none">{flag}</span>
      <span className="flex-1">{label}</span>
      <span className="text-xs text-muted-foreground">{selected ? "Current" : locale.code}</span>
    </>
  )}
/>

翻訳された UI テキスト

検索欄のプレースホルダー、空の状態、アクセシブルなラベルは、デフォルトで英語です。 翻訳するには labels を渡します。 省略したキーは英語のデフォルトのままになります。 searchPlaceholder と emptyMessage の props も引き続き使用でき、両方が設定されている場合は labels より優先されます。

"use client";

import { LanguageSwitcher } from "@/components/ui/language-switcher";

export default function App() {
  return (
    <LanguageSwitcher
      locales={[{ code: "en" }, { code: "fr" }, { code: "es" }]}
      defaultValue="fr"
      label="native"
      labels={{
        searchPlaceholder: "Rechercher une langue…",
        searchLabel: "Rechercher une langue",
        clearSearch: "Effacer la recherche",
        emptyMessage: "Aucune langue trouvée.",
        listbox: "Choisir la langue",
        current: (name) => `Langue actuelle : ${name}`,
      }}
    />
  );
}
キーデフォルト用途
searchPlaceholder"Search languages..."検索欄のプレースホルダー
searchLabel"Search languages"検索欄のアクセシブルなラベル
clearSearch"Clear search"クリアボタンのアクセシブルなラベル
emptyMessage"No languages found."一致する項目がない場合に表示
listbox"Select language"選択肢一覧のアクセシブルなラベル
current(name) => "Current language: " + nameトリガーのアクセシブルなラベル

キーボード操作

ポップアップは WAI-ARIA のコンボボックスパターンに従います。 フォーカスは検索欄に留まり、検索欄は role="combobox" として公開されます。 ハイライトされた選択肢は aria-activedescendant を通じて読み上げられます。

キー操作
Enter、Space、またはクリックトリガーからポップアップを開きます
ArrowDown / ArrowUp(閉じている状態)最初または最後のロケールがハイライトされた状態で開きます
ArrowDown / ArrowUpハイライトを移動します。両端で循環します
Home / End最初または最後のロケールをハイライトします
Enterハイライトされたロケールを選択し、フォーカスをトリガーに戻します
Escapeポップアップを閉じ、フォーカスをトリガーに戻します
入力名前またはコードで絞り込みます。ハイライトは先頭に戻ります

ハイライトは、メニューを開いたときに表示されていた行に留まります。 ネイティブのメニューのように、一覧はその下でスクロールします。 ページ自体はスクロールしません。maxHeight を渡すと、スクロールが始まるまでの一覧の高さを変更できます。

機能

  • 設定不要 — コードを渡すだけで、ラベルと国旗は自動的に解決されます
  • 検索付きドロップダウン — 名前またはコードでロケールを絞り込み
  • RTL 対応 — RTL のロケール(ar、he、fa、ur)を自動検出し、レイアウトを調整します
  • キーボードでの操作に対応 — 矢印キー、Home / End、Enter、Escape で操作可能
  • ARIA ラベル — aria-activedescendant を持つ role="combobox" 検索欄に加え、aria-expanded、aria-haspopup、aria-selected
  • ダークモード — Tailwind CSS で構築されており、ダークモードに対応しています
  • ライブラリ非依存 — コールバックを通じて、任意の i18n 設定で動作します

対応している言語

ラベルと国旗は言語コードから自動検出されます。コードだけを渡してください:

コード言語国旗RTL
enEnglish🇺🇸
frFrançais🇫🇷
esEspañol🇪🇸
deDeutsch🇩🇪
ja日本語🇯🇵
ko한국어🇰🇷
zh中文🇨🇳
arالعربية🇸🇦✓
ptPortuguês🇧🇷
itItaliano🇮🇹
nlNederlands🇳🇱
ruРусский🇷🇺
plPolski🇵🇱
trTürkçe🇹🇷
viTiếng Việt🇻🇳
thไทย🇹🇭
idBahasa Indonesia🇮🇩
hiहिन्दी🇮🇳
bnবাংলা🇧🇩
ukУкраїнська🇺🇦
csČeština🇨🇿
svSvenska🇸🇪
daDansk🇩🇰
fiSuomi🇫🇮
noNorsk🇳🇴
elΕλληνικά🇬🇷
heעברית🇮🇱✓
faفارسی🇮🇷✓
roRomână🇷🇴
huMagyar🇭🇺
skSlovenčina🇸🇰
bgБългарски🇧🇬
hrHrvatski🇭🇷
srСрпски🇷🇸
slSlovenščina🇸🇮
ltLietuvių🇱🇹
lvLatviešu🇱🇻
etEesti🇪🇪
gaGaeilge🇮🇪
caCatalà🇪🇸
isÍslenska🇮🇸
swKiswahili🇹🇿
amአማርኛ🇪🇹
neनेपाली🇳🇵
siසිංහල🇱🇰
myမြန်မာ🇲🇲
kmខ្មែរ🇰🇭
loລາວ🇱🇦
kaქართული🇬🇪
hyՀայերեն🇦🇲
kkҚазақ🇰🇿
uzOʻzbek🇺🇿
mnМонгол🇲🇳
urاردو🇵🇰✓
mlമലയാളം🇮🇳
taதமிழ்🇮🇳
teతెలుగు🇮🇳
knಕನ್ನಡ🇮🇳
mrमराठी🇮🇳
guગુજરાતી🇮🇳
paਪੰਜਾਬੀ🇮🇳
yoYorùbá🇳🇬
igIgbo🇳🇬
zuisiZulu🇿🇦
afAfrikaans🇿🇦
haHausa🇳🇬
tlFilipino🇵🇭
miTe Reo Māori🇳🇿
smGagana Samoa🇼🇸
toLea Faka-Tonga🇹🇴
eoEsperanto🌐

一覧にない言語が必要ですか? カスタムの label と flag を渡して上書きします:

<LanguageSwitcher
  locales={[{ code: "xyz", label: "My Language", flag: "🏴" }]}
  defaultValue="xyz"
/>

依存関係

このコンポーネントは次を使用しています:

  • clsx と tailwind-merge(dependencies としてバンドル)

peerDependencies:

  • react >= 18
  • react-dom >= 18
  • tailwindcss >= 3

クレジット

作成者: sapatmohit。

このページの内容