# Language Picker

> A searchable language picker with native names and RTL metadata.

A shadcn/ui block from the [@flagcn](https://flagcn.dev) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/flagcn/language-picker).

- Page, with a live preview: https://shadcn.exchange/r/flagcn/language-picker
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/flagcn/language-picker-light-1a35e83b76d66dac.png
- Registry item: https://flagcn.dev/r/language-picker.json
- Categories: languages, forms, combobox
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://flagcn.dev/r/language-picker.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `@tabler/icons-react`
- Registry items: `button`, `input`, `badge`

## Notes from the registry

Constrain the picker with languages and inspect RTL direction through language-utils.

## Files

### `src/components/flags/language-picker.tsx`

```tsx
// SPDX-License-Identifier: MIT

import { Badge } from "@/components/ui/badge"
import * as React from "react"

import { languageData, type LanguageCode } from "./data/languages"
import { SearchPicker, type SearchPickerProps } from "./search-picker"

export interface LanguagePickerProps extends Omit<SearchPickerProps, "items" | "value" | "defaultValue" | "onValueChange" | "placeholder" | "searchPlaceholder" | "emptyMessage"> {
  value?: LanguageCode
  defaultValue?: LanguageCode
  onValueChange?: (code: LanguageCode) => void
  languages?: readonly LanguageCode[]
  placeholder?: string
  searchPlaceholder?: string
  emptyMessage?: string
}

export function LanguagePicker({
  languages,
  placeholder = "Select a language",
  searchPlaceholder = "Search languages…",
  emptyMessage = "No languages found.",
  onValueChange,
  ...props
}: LanguagePickerProps) {
  const items = React.useMemo(() => {
    const allowed = languages ? new Set(languages) : undefined
    return languageData.filter((language) => !allowed || allowed.has(language.code)).map((language) => ({
      value: language.code,
      label: language.name,
      description: language.nativeName,
      searchValue: language.nativeName,
      trailing: language.direction === "rtl" ? <Badge variant="outline" className="font-mono text-[10px]">RTL</Badge> : undefined,
    }))
  }, [languages])

  return <SearchPicker items={items} placeholder={placeholder} searchPlaceholder={searchPlaceholder} emptyMessage={emptyMessage} onValueChange={(code) => onValueChange?.(code as LanguageCode)} {...props} />
}
```

### `src/components/flags/search-picker.tsx`

```tsx
// SPDX-License-Identifier: MIT

import { IconCheck, IconChevronDown, IconSearch } from "@tabler/icons-react"
import * as React from "react"

import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { cn } from "@/lib/utils"

export interface SearchPickerItem {
  value: string
  label: string
  description?: string
  searchValue?: string
  leading?: React.ReactNode
  trailing?: React.ReactNode
  disabled?: boolean
}

export interface SearchPickerProps {
  items: readonly SearchPickerItem[]
  value?: string
  defaultValue?: string
  onValueChange?: (value: string) => void
  placeholder: string
  searchPlaceholder: string
  emptyMessage: string
  disabled?: boolean
  name?: string
  className?: string
  triggerClassName?: string
  contentClassName?: string
  "aria-label"?: string
}

export function SearchPicker({
  items,
  value,
  defaultValue,
  onValueChange,
  placeholder,
  searchPlaceholder,
  emptyMessage,
  disabled,
  name,
  className,
  triggerClassName,
  contentClassName,
  "aria-label": ariaLabel,
}: SearchPickerProps) {
  const [internalValue, setInternalValue] = React.useState(defaultValue)
  const [query, setQuery] = React.useState("")
  const [phase, setPhase] = React.useState<"closed" | "open" | "closing">("closed")
  const [activeIndex, setActiveIndex] = React.useState(0)
  const rootRef = React.useRef<HTMLDivElement>(null)
  const triggerRef = React.useRef<HTMLButtonElement>(null)
  const searchRef = React.useRef<HTMLInputElement>(null)
  const closeTimerRef = React.useRef<number | undefined>(undefined)
  const listId = React.useId()
  const currentValue = value ?? internalValue
  const selectedItem = items.find((item) => item.value === currentValue)
  const open = phase === "open"

  const filteredItems = React.useMemo(() => {
    const normalized = query.trim().toLocaleLowerCase()
    if (!normalized) return items
    return items.filter((item) => `${item.label} ${item.description ?? ""} ${item.value} ${item.searchValue ?? ""}`.toLocaleLowerCase().includes(normalized))
  }, [items, query])

  const close = React.useCallback((restoreFocus = false) => {
    window.clearTimeout(closeTimerRef.current)
    setPhase((current) => current === "closed" ? current : "closing")
    closeTimerRef.current = window.setTimeout(() => {
      setPhase("closed")
      if (restoreFocus) triggerRef.current?.focus()
    }, 150)
  }, [])

  React.useEffect(() => () => window.clearTimeout(closeTimerRef.current), [])

  React.useEffect(() => {
    if (!open) return
    const onPointerDown = (event: PointerEvent) => {
      if (!rootRef.current?.contains(event.target as Node)) close()
    }
    document.addEventListener("pointerdown", onPointerDown)
    return () => document.removeEventListener("pointerdown", onPointerDown)
  }, [close, open])

  React.useEffect(() => {
    if (open) requestAnimationFrame(() => searchRef.current?.focus())
  }, [open])

  function selectItem(item: SearchPickerItem) {
    if (item.disabled) return
    if (value === undefined) setInternalValue(item.value)
    onValueChange?.(item.value)
    setQuery("")
    close(true)
  }

  return (
    <div ref={rootRef} data-slot="search-picker" className={cn("relative min-w-0 w-full", className)}>
      {name ? <input type="hidden" name={name} value={currentValue ?? ""} /> : null}
      <Button
        ref={triggerRef}
        type="button"
        variant="outline"
        role="combobox"
        aria-expanded={open}
        aria-controls={listId}
        aria-haspopup="listbox"
        aria-label={ariaLabel ?? placeholder}
        disabled={disabled}
        className={cn("h-10 min-w-0 w-full justify-between px-3 font-normal sm:h-9", triggerClassName)}
        onClick={() => {
          if (open) close()
          else {
            window.clearTimeout(closeTimerRef.current)
            setActiveIndex(0)
            setPhase("open")
          }
        }}
      >
        <span className={cn("flex min-w-0 items-center gap-2", !selectedItem && "text-muted-foreground")}>
          {selectedItem?.leading}
          <span className="truncate">{selectedItem?.label ?? placeholder}</span>
        </span>
        <IconChevronDown data-icon="inline-end" aria-hidden="true" />
      </Button>

      {phase !== "closed" ? (
        <div
          data-origin="top"
          className={cn("t-dropdown absolute z-50 mt-1 w-full min-w-64 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md", phase === "open" ? "is-open" : "is-closing", contentClassName)}
          onKeyDown={(event) => {
            if (event.key === "Escape") close(true)
            if (event.key === "ArrowDown") {
              event.preventDefault()
              setActiveIndex((index) => Math.min(index + 1, filteredItems.length - 1))
            }
            if (event.key === "ArrowUp") {
              event.preventDefault()
              setActiveIndex((index) => Math.max(index - 1, 0))
            }
            if (event.key === "Enter" && filteredItems[activeIndex]) {
              event.preventDefault()
              selectItem(filteredItems[activeIndex])
            }
          }}
        >
          <div className="relative border-b p-2">
            <IconSearch aria-hidden="true" className="pointer-events-none absolute start-4 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
            <Input
              ref={searchRef}
              value={query}
              onChange={(event) => { setQuery(event.target.value); setActiveIndex(0) }}
              placeholder={searchPlaceholder}
              aria-label={searchPlaceholder}
              aria-controls={listId}
              aria-activedescendant={filteredItems[activeIndex] ? `${listId}-${filteredItems[activeIndex].value}` : undefined}
              className="h-9 ps-8 shadow-none"
            />
          </div>
          <div id={listId} role="listbox" className="max-h-72 overflow-y-auto p-1">
            {filteredItems.length ? filteredItems.map((item, index) => (
              <button
                key={item.value}
                id={`${listId}-${item.value}`}
                type="button"
                role="option"
                aria-selected={item.value === currentValue}
                disabled={item.disabled}
                data-active={index === activeIndex || undefined}
                className="flex min-h-10 w-full items-center gap-2 rounded-sm px-2 py-2 text-start text-sm outline-none hover:bg-accent hover:text-accent-foreground data-[active]:bg-accent data-[active]:text-accent-foreground focus-visible:ring-ring/30 focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-40 sm:min-h-9"
                onPointerMove={() => setActiveIndex(index)}
                onClick={() => selectItem(item)}
              >
                {item.leading}
                <span className="min-w-0 flex-1">
                  <span className="block truncate">{item.label}</span>
                  {item.description ? <span className="block truncate text-xs text-muted-foreground">{item.description}</span> : null}
                </span>
                {item.trailing}
                <IconCheck aria-hidden="true" className={cn("size-4", item.value === currentValue ? "opacity-100" : "opacity-0")} />
              </button>
            )) : <p className="px-3 py-8 text-center text-sm text-muted-foreground">{emptyMessage}</p>}
          </div>
        </div>
      ) : null}
    </div>
  )
}
```

### `src/components/flags/data/languages.ts`

```ts
// SPDX-License-Identifier: MIT

// Generated by scripts/generate-country-data.mjs from countries-list,
// i18n-iso-countries, and libphonenumber-js. Do not edit by hand.

export const languageData = [
  {
    "code": "ab",
    "name": "Abkhazian",
    "nativeName": "Аҧсуа",
    "direction": "ltr"
  },
  {
    "code": "aa",
    "name": "Afar",
    "nativeName": "Afar",
    "direction": "ltr"
  },
  {
    "code": "af",
    "name": "Afrikaans",
    "nativeName": "Afrikaans",
    "direction": "ltr"
  },
  {
    "code": "ak",
    "name": "Akan",
    "nativeName": "Akana",
    "direction": "ltr"
  },
  {
    "code": "sq",
    "name": "Albanian",
    "nativeName": "Shqip",
    "direction": "ltr"
  },
  {
    "code": "am",
    "name": "Amharic",
    "nativeName": "አማርኛ",
    "direction": "ltr"
  },
  {
    "code": "ar",
    "name": "Arabic",
    "nativeName": "العربية",
    "direction": "rtl"
  },
  {
    "code": "an",
    "name": "Aragonese",
    "nativeName": "Aragonés",
    "direction": "ltr"
  },
  {
    "code": "hy",
    "name": "Armenian",
    "nativeName": "Հայերեն",
    "direction": "ltr"
  },
  {
    "code": "as",
    "name": "Assamese",
    "nativeName": "অসমীয়া",
    "direction": "ltr"
  },
  {
    "code": "av",
    "name": "Avar",
    "nativeName": "Авар",
    "direction": "ltr"
  },
  {
    "code": "ay",
    "name": "Aymara",
    "nativeName": "Aymar",
    "direction": "ltr"
  },
  {
    "code": "az",
    "name": "Azerbaijani",
    "nativeName": "Azərbaycanca / آذربايجان",
    "direction": "ltr"
  },
  {
    "code": "bm",
    "name": "Bambara",
    "nativeName": "Bamanankan",
    "direction": "ltr"
  },
  {
    "code": "ba",
    "name": "Bashkir",
    "nativeName": "Башҡорт",
    "direction": "ltr"
  },
  {
    "code": "eu",
    "name": "Basque",
    "nativeName": "Euskara",
    "direction": "ltr"
  },
  {
    "code": "be",
    "name": "Belarusian",
    "nativeName": "Беларуская",
    "direction": "ltr"
  },
  {
    "code": "bn",
    "name": "Bengali",
    "nativeName": "বাংলা",
    "direction": "ltr"
  },
  {
    "code": "bh",
    "name": "Bihari",
    "nativeName": "भोजपुरी",
    "direction": "ltr"
  },
  {
    "code": "bi",
    "name": "Bislama",
    "nativeName": "Bislama",
    "direction": "ltr"
  },
  {
    "code": "bs",
    "name": "Bosnian",
    "nativeName": "Bosanski",
    "direction": "ltr"
  },
  {
    "code": "br",
    "name": "Breton",
    "nativeName": "Brezhoneg",
    "direction": "ltr"
  },
  {
    "code": "bg",
    "name": "Bulgarian",
    "nativeName": "Български",
    "direction": "ltr"
  },
  {
    "code": "my",
    "name": "Burmese",
    "nativeName": "မြန်မာစာ",
    "direction": "ltr"
  },
  {
    "code": "km",
    "name": "Cambodian",
    "nativeName": "ភាសាខ្មែរ",
    "direction": "ltr"
  },
  {
    "code": "ca",
    "name": "Catalan",
    "nativeName": "Català",
    "direction": "ltr"
  },
  {
    "code": "ch",
    "name": "Chamorro",
    "nativeName": "Chamoru",
    "direction": "ltr"
  },
  {
    "code": "ce",
    "name": "Chechen",
    "nativeName": "Нохчийн",
    "direction": "ltr"
  },
  {
    "code": "ny",
    "name": "Chichewa",
    "nativeName": "Chi-Chewa",
    "direction": "ltr"
  },
  {
    "code": "zh",
    "name": "Chinese",
    "nativeName": "中文",
    "direction": "ltr"
  },
  {
    "code": "cv",
    "name": "Chuvash",
    "nativeName": "Чăваш",
    "direction": "ltr"
  },
  {
    "code": "kw",
    "name": "Cornish",
    "nativeName": "Kernewek",
    "direction": "ltr"
  },
  {
    "code": "co",
    "name": "Corsican",
    "nativeName": "Corsu",
    "direction": "ltr"
  },
  {
    "code": "cr",
    "name": "Cree",
    "nativeName": "Nehiyaw",
    "direction": "ltr"
  },
  {
    "code": "hr",
    "name": "Croatian",
    "nativeName": "Hrvatski",
    "direction": "ltr"
  },
  {
    "code": "cs",
    "name": "Czech",
    "nativeName": "Čeština",
    "direction": "ltr"
  },
  {
    "code": "da",
    "name": "Danish",
    "nativeName": "Dansk",
    "direction": "ltr"
  },
  {
    "code": "dv",
    "name": "Divehi",
    "nativeName": "ދިވެހިބަސް",
    "direction": "rtl"
  },
  {
    "code": "nl",
    "name": "Dutch",
    "nativeName": "Nederlands",
    "direction": "ltr"
  },
  {
    "code": "dz",
    "name": "Dzongkha",
    "nativeName": "ཇོང་ཁ",
    "direction": "ltr"
  },
  {
    "code": "en",
    "name": "English",
    "nativeName": "English",
    "direction": "ltr"
  },
  {
    "code": "eo",
    "name": "Esperanto",
    "nativeName": "Esperanto",
    "direction": "ltr"
  },
  {
    "code": "et",
    "name": "Estonian",
    "nativeName": "Eesti",
    "direction": "ltr"
  },
  {
    "code": "ee",
    "name": "Ewe",
    "nativeName": "Ɛʋɛ",
    "direction": "ltr"
  },
  {
    "code": "fo",
    "name": "Faroese",
    "nativeName": "Føroyskt",
    "direction": "ltr"
  },
  {
    "code": "fj",
    "name": "Fijian",
    "nativeName": "Na Vosa Vakaviti",
    "direction": "ltr"
  },
  {
    "code": "fi",
    "name": "Finnish",
    "nativeName": "Suomi",
    "direction": "ltr"
  },
  {
    "code": "fr",
    "name": "French",
    "nativeName": "Français",
    "direction": "ltr"
  },
  {
    "code": "gl",
    "name": "Galician",
    "nativeName": "Galego",
    "direction": "ltr"
  },
  {
    "code": "lg",
    "name": "Ganda",
    "nativeName": "Luganda",
    "direction": "ltr"
  },
  {
    "code": "ka",
    "name": "Georgian",
    "nativeName": "ქართული",
    "direction": "ltr"
  },
  {
    "code": "de",
    "name": "German",
    "nativeName": "Deutsch",
    "direction": "ltr"
  },
  {
    "code": "el",
    "name": "Greek",
    "nativeName": "Ελληνικά",
    "direction": "ltr"
  },
  {
    "code": "kl",
    "name": "Greenlandic",
    "nativeName": "Kalaallisut",
    "direction": "ltr"
  },
  {
    "code": "gn",
    "name": "Guarani",
    "nativeName": "Avañe'ẽ",
    "direction": "ltr"
  },
  {
    "code": "gu",
    "name": "Gujarati",
    "nativeName": "ગુજરાતી",
    "direction": "ltr"
  },
  {
    "code": "ht",
    "name": "Haitian",
    "nativeName": "Krèyol ayisyen",
    "direction": "ltr"
  },
  {
    "code": "ha",
    "name": "Hausa",
    "nativeName": "هَوُسَ",
    "direction": "rtl"
  },
  {
    "code": "he",
    "name": "Hebrew",
    "nativeName": "עברית",
    "direction": "rtl"
  },
  {
    "code": "hz",
    "name": "Herero",
    "nativeName": "Otsiherero",
    "direction": "ltr"
  },
  {
    "code": "hi",
    "name": "Hindi",
    "nativeName": "हिन्दी",
    "direction": "ltr"
  },
  {
    "code": "ho",
    "name": "Hiri Motu",
    "nativeName": "Hiri Motu",
    "direction": "ltr"
  },
  {
    "code": "hu",
    "name": "Hungarian",
    "nativeName": "Magyar",
    "direction": "ltr"
  },
  {
    "code": "is",
    "name": "Icelandic",
    "nativeName": "Íslenska",
    "direction": "ltr"
  },
  {
    "code": "io",
    "name": "Ido",
    "nativeName": "Ido",
    "direction": "ltr"
  },
  {
    "code": "ig",
    "name": "Igbo",
    "nativeName": "Igbo",
    "direction": "ltr"
  },
  {
    "code": "id",
    "name": "Indonesian",
    "nativeName": "Bahasa Indonesia",
    "direction": "ltr"
  },
  {
    "code": "ia",
    "name": "Interlingua",
    "nativeName": "Interlingua",
    "direction": "ltr"
  },
  {
    "code": "ie",
    "name": "Interlingue",
    "nativeName": "Interlingue",
    "direction": "ltr"
  },
  {
    "code": "iu",
    "name": "Inuktitut",
    "nativeName": "ᐃᓄᒃᑎᑐᑦ",
    "direction": "ltr"
  },
  {
    "code": "ik",
    "name": "Inupiak",
    "nativeName": "Iñupiak",
    "direction": "ltr"
  },
  {
    "code": "ga",
    "name": "Irish",
    "nativeName": "Gaeilge",
    "direction": "ltr"
  },
  {
    "code": "it",
    "name": "Italian",
    "nativeName": "Italiano",
    "direction": "ltr"
  },
  {
    "code": "ja",
    "name": "Japanese",
    "nativeName": "日本語",
    "direction": "ltr"
  },
  {
    "code": "jv",
    "name": "Javanese",
    "nativeName": "Basa Jawa",
    "direction": "ltr"
  },
  {
    "code": "kn",
    "name": "Kannada",
    "nativeName": "ಕನ್ನಡ",
    "direction": "ltr"
  },
  {
    "code": "kr",
    "name": "Kanuri",
    "nativeName": "Kanuri",
    "direction": "ltr"
  },
  {
    "code": "ks",
    "name": "Kashmiri",
    "nativeName": "कश्मीरी / كشميري",
    "direction": "rtl"
  },
  {
    "code": "kk",
    "name": "Kazakh",
    "nativeName": "Қазақша",
    "direction": "ltr"
  },
  {
    "code": "ki",
    "name": "Kikuyu",
    "nativeName": "Gĩkũyũ",
    "direction": "ltr"
  },
  {
    "code": "rn",
    "name": "Kirundi",
    "nativeName": "Kirundi",
    "direction": "ltr"
  },
  {
    "code": "kv",
    "name": "Komi",
    "nativeName": "Коми",
    "direction": "ltr"
  },
  {
    "code": "kg",
    "name": "Kongo",
    "nativeName": "KiKongo",
    "direction": "ltr"
  },
  {
    "code": "ko",
    "name": "Korean",
    "nativeName": "한국어",
    "direction": "ltr"
  },
  {
    "code": "kj",
    "name": "Kuanyama",
    "nativeName": "Kuanyama",
    "direction": "ltr"
  },
  {
    "code": "ku",
    "name": "Kurdish",
    "nativeName": "Kurdî / كوردی",
    "direction": "rtl"
  },
  {
    "code": "ky",
    "name": "Kyrgyz",
    "nativeName": "Кыргызча",
    "direction": "ltr"
  },
  {
    "code": "lo",
    "name": "Laotian",
    "nativeName": "ລາວ / Pha xa lao",
    "direction": "ltr"
  },
  {
    "code": "la",
    "name": "Latin",
    "nativeName": "Latina",
    "direction": "ltr"
  },
  {
    "code": "lv",
    "name": "Latvian",
    "nativeName": "Latviešu",
    "direction": "ltr"
  },
  {
    "code": "li",
    "name": "Limburgian",
    "nativeName": "Limburgs",
    "direction": "ltr"
  },
  {
    "code": "ln",
    "name": "Lingala",
    "nativeName": "Lingála",
    "direction": "ltr"
  },
  {
    "code": "lt",
    "name": "Lithuanian",
    "nativeName": "Lietuvių",
    "direction": "ltr"
  },
  {
    "code": "lu",
    "name": "Luba-Katanga",
    "nativeName": "Tshiluba",
    "direction": "ltr"
  },
  {
    "code": "lb",
    "name": "Luxembourgish",
    "nativeName": "Lëtzebuergesch",
    "direction": "ltr"
  },
  {
    "code": "mk",
    "name": "Macedonian",
    "nativeName": "Македонски",
    "direction": "ltr"
  },
  {
    "code": "mg",
    "name": "Malagasy",
    "nativeName": "Malagasy",
    "direction": "ltr"
  },
  {
    "code": "ms",
    "name": "Malay",
    "nativeName": "Bahasa Melayu",
    "direction": "ltr"
  },
  {
    "code": "ml",
    "name": "Malayalam",
    "nativeName": "മലയാളം",
    "direction": "ltr"
  },
  {
    "code": "mt",
    "name": "Maltese",
    "nativeName": "bil-Malti",
    "direction": "ltr"
  },
  {
    "code": "gv",
    "name": "Manx",
    "nativeName": "Gaelg",
    "direction": "ltr"
  },
  {
    "code": "mi",
    "name": "Maori",
    "nativeName": "Māori",
    "direction": "ltr"
  },
  {
    "code": "mr",
    "name": "Marathi",
    "nativeName": "मराठी",
    "direction": "ltr"
  },
  {
    "code": "mh",
    "name": "Marshallese",
    "nativeName": "Kajin Majel / Ebon",
    "direction": "ltr"
  },
  {
    "code": "mo",
    "name": "Moldovan",
    "nativeName": "Moldovenească",
    "direction": "ltr"
  },
  {
    "code": "mn",
    "name": "Mongolian",
    "nativeName": "Монгол",
    "direction": "ltr"
  },
  {
    "code": "na",
    "name": "Nauruan",
    "nativeName": "Dorerin Naoero",
    "direction": "ltr"
  },
  {
    "code": "nv",
    "name": "Navajo",
    "nativeName": "Diné bizaad",
    "direction": "ltr"
  },
  {
    "code": "ng",
    "name": "Ndonga",
    "nativeName": "Oshiwambo",
    "direction": "ltr"
  },
  {
    "code": "ne",
    "name": "Nepali",
    "nativeName": "नेपाली",
    "direction": "ltr"
  },
  {
    "code": "nd",
    "name": "North Ndebele",
    "nativeName": "Sindebele",
    "direction": "ltr"
  },
  {
    "code": "se",
    "name": "Northern Sami",
    "nativeName": "Sámegiella",
    "direction": "ltr"
  },
  {
    "code": "no",
    "name": "Norwegian",
    "nativeName": "Norsk",
    "direction": "ltr"
  },
  {
    "code": "nb",
    "name": "Norwegian Bokmål",
    "nativeName": "Norsk bokmål",
    "direction": "ltr"
  },
  {
    "code": "nn",
    "name": "Norwegian Nynorsk",
    "nativeName": "Norsk nynorsk",
    "direction": "ltr"
  },
  {
    "code": "oc",
    "name": "Occitan",
    "nativeName": "Occitan",
    "direction": "ltr"
  },
  {
    "code": "oj",
    "name": "Ojibwa",
    "nativeName": "ᐊᓂᔑᓈᐯᒧᐎᓐ / Anishinaabemowin",
    "direction": "ltr"
  },
  {
    "code": "cu",
    "name": "Old Church Slavonic / Old Bulgarian",
    "nativeName": "словѣньскъ / slověnĭskŭ",
    "direction": "ltr"
  },
  {
    "code": "or",
    "name": "Oriya",
    "nativeName": "ଓଡ଼ିଆ",
    "direction": "ltr"
  },
  {
    "code": "om",
    "name": "Oromo",
    "nativeName": "Oromoo",
    "direction": "ltr"
  },
  {
    "code": "os",
    "name": "Ossetian / Ossetic",
    "nativeName": "Иронау",
    "direction": "ltr"
  },
  {
    "code": "pi",
    "name": "Pali",
    "nativeName": "Pāli / पाऴि",
    "direction": "ltr"
  },
  {
    "code": "pa",
    "name": "Panjabi / Punjabi",
    "nativeName": "ਪੰਜਾਬੀ / पंजाबी / پنجابي",
    "direction": "ltr"
  },
  {
    "code": "ps",
    "name": "Pashto",
    "nativeName": "پښتو",
    "direction": "rtl"
  },
  {
    "code": "fa",
    "name": "Persian",
    "nativeName": "فارسی",
    "direction": "rtl"
  },
  {
    "code": "ff",
    "name": "Peul",
    "nativeName": "Fulfulde",
    "direction": "ltr"
  },
  {
    "code": "pl",
    "name": "Polish",
    "nativeName": "Polski",
    "direction": "ltr"
  },
  {
    "code": "pt",
    "name": "Portuguese",
    "nativeName": "Português",
    "direction": "ltr"
  },
  {
    "code": "qu",
    "name": "Quechua",
    "nativeName": "Runa Simi",
    "direction": "ltr"
  },
  {
    "code": "rm",
    "name": "Raeto Romance",
    "nativeName": "Rumantsch",
    "direction": "ltr"
  },
  {
    "code": "ro",
    "name": "Romanian",
    "nativeName": "Română",
    "direction": "ltr"
  },
  {
    "code": "ru",
    "name": "Russian",
    "nativeName": "Русский",
    "direction": "ltr"
  },
  {
    "code": "rw",
    "name": "Rwandi",
    "nativeName": "Kinyarwandi",
    "direction": "ltr"
  },
  {
    "code": "sm",
    "name": "Samoan",
    "nativeName": "Gagana Samoa",
    "direction": "ltr"
  },
  {
    "code": "sg",
    "name": "Sango",
    "nativeName": "Sängö",
    "direction": "ltr"
  },
  {
    "code": "sa",
    "name": "Sanskrit",
    "nativeName": "संस्कृतम्",
    "direction": "ltr"
  },
  {
    "code": "sc",
    "name": "Sardinian",
    "nativeName": "Sardu",
    "direction": "ltr"
  },
  {
    "code": "gd",
    "name": "Scottish Gaelic",
    "nativeName": "Gàidhlig",
    "direction": "ltr"
  },
  {
    "code": "sr",
    "name": "Serbian",
    "nativeName": "Српски",
    "direction": "ltr"
  },
  {
    "code": "sh",
    "name": "Serbo-Croatian",
    "nativeName": "Srpskohrvatski / Српскохрватски",
    "direction": "ltr"
  },
  {
    "code": "sn",
    "name": "Shona",
    "nativeName": "chiShona",
    "direction": "ltr"
  },
  {
    "code": "ii",
    "name": "Sichuan Yi",
    "nativeName": "ꆇꉙ / 四川彝语",
    "direction": "ltr"
  },
  {
    "code": "sd",
    "name": "Sindhi",
    "nativeName": "सिनधि",
    "direction": "ltr"
  },
  {
    "code": "si",
    "name": "Sinhalese",
    "nativeName": "සිංහල",
    "direction": "ltr"
  },
  {
    "code": "sk",
    "name": "Slovak",
    "nativeName": "Slovenčina",
    "direction": "ltr"
  },
  {
    "code": "sl",
    "name": "Slovenian",
    "nativeName": "Slovenščina",
    "direction": "ltr"
  },
  {
    "code": "so",
    "name": "Somalia",
    "nativeName": "Soomaaliga",
    "direction": "ltr"
  },
  {
    "code": "nr",
    "name": "South Ndebele",
    "nativeName": "isiNdebele",
    "direction": "ltr"
  },
  {
    "code": "st",
    "name": "Southern Sotho",
    "nativeName": "Sesotho",
    "direction": "ltr"
  },
  {
    "code": "es",
    "name": "Spanish",
    "nativeName": "Español",
    "direction": "ltr"
  },
  {
    "code": "su",
    "name": "Sundanese",
    "nativeName": "Basa Sunda",
    "direction": "ltr"
  },
  {
    "code": "sw",
    "name": "Swahili",
    "nativeName": "Kiswahili",
    "direction": "ltr"
  },
  {
    "code": "ss",
    "name": "Swati",
    "nativeName": "SiSwati",
    "direction": "ltr"
  },
  {
    "code": "sv",
    "name": "Swedish",
    "nativeName": "Svenska",
    "direction": "ltr"
  },
  {
    "code": "tl",
    "name": "Tagalog / Filipino",
    "nativeName": "Tagalog",
    "direction": "ltr"
  },
  {
    "code": "ty",
    "name": "Tahitian",
    "nativeName": "Reo Mā`ohi",
    "direction": "ltr"
  },
  {
    "code": "tg",
    "name": "Tajik",
    "nativeName": "Тоҷикӣ",
    "direction": "ltr"
  },
  {
    "code": "ta",
    "name": "Tamil",
    "nativeName": "தமிழ்",
    "direction": "ltr"
  },
  {
    "code": "tt",
    "name": "Tatar",
    "nativeName": "Tatarça",
    "direction": "ltr"
  },
  {
    "code": "te",
    "name": "Telugu",
    "nativeName": "తెలుగు",
    "direction": "ltr"
  },
  {
    "code": "th",
    "name": "Thai",
    "nativeName": "ไทย / Phasa Thai",
    "direction": "ltr"
  },
  {
    "code": "bo",
    "name": "Tibetan",
    "nativeName": "བོད་ཡིག / Bod skad",
    "direction": "ltr"
  },
  {
    "code": "ti",
    "name": "Tigrinya",
    "nativeName": "ትግርኛ",
    "direction": "ltr"
  },
  {
    "code": "to",
    "name": "Tonga",
    "nativeName": "Lea Faka-Tonga",
    "direction": "ltr"
  },
  {
    "code": "ts",
    "name": "Tsonga",
    "nativeName": "Xitsonga",
    "direction": "ltr"
  },
  {
    "code": "tn",
    "name": "Tswana",
    "nativeName": "Setswana",
    "direction": "ltr"
  },
  {
    "code": "tr",
    "name": "Turkish",
    "nativeName": "Türkçe",
    "direction": "ltr"
  },
  {
    "code": "tk",
    "name": "Turkmen",
    "nativeName": "Туркмен / تركمن",
    "direction": "ltr"
  },
  {
    "code": "tw",
    "name": "Twi",
    "nativeName": "Twi",
    "direction": "ltr"
  },
  {
    "code": "uk",
    "name": "Ukrainian",
    "nativeName": "Українська",
    "direction": "ltr"
  },
  {
    "code": "ur",
    "name": "Urdu",
    "nativeName": "اردو",
    "direction": "rtl"
  },
  {
    "code": "ug",
    "name": "Uyghur",
    "nativeName": "Uyƣurqə / ئۇيغۇرچە",
    "direction": "ltr"
  },
  {
    "code": "uz",
    "name": "Uzbek",
    "nativeName": "O'zbekcha",
    "direction": "ltr"
  },
  {
    "code": "ve",
    "name": "Venda",
    "nativeName": "Tshivenḓa",
    "direction": "ltr"
  },
  {
    "code": "vi",
    "name": "Vietnamese",
    "nativeName": "Tiếng Việt",
    "direction": "ltr"
  },
  {
    "code": "vo",
    "name": "Volapük",
    "nativeName": "Volapük",
    "direction": "ltr"
  },
  {
    "code": "wa",
    "name": "Walloon",
    "nativeName": "Walon",
    "direction": "ltr"
  },
  {
    "code": "cy",
    "name": "Welsh",
    "nativeName": "Cymraeg",
    "direction": "ltr"
  },
  {
    "code": "fy",
    "name": "West Frisian",
    "nativeName": "Frysk",
    "direction": "ltr"
  },
  {
    "code": "wo",
    "name": "Wolof",
    "nativeName": "Wollof",
    "direction": "ltr"
  },
  {
    "code": "xh",
    "name": "Xhosa",
    "nativeName": "isiXhosa",
    "direction": "ltr"
  },
  {
    "code": "yi",
    "name": "Yiddish",
    "nativeName": "ייִדיש",
    "direction": "rtl"
  },
  {
    "code": "yo",
    "name": "Yoruba",
    "nativeName": "Yorùbá",
    "direction": "ltr"
  },
  {
    "code": "za",
    "name": "Zhuang",
    "nativeName": "Cuengh / Tôô / 壮语",
    "direction": "ltr"
  },
  {
    "code": "zu",
    "name": "Zulu",
    "nativeName": "isiZulu",
    "direction": "ltr"
  }
] as const

export type Language = (typeof languageData)[number]
export type LanguageCode = Language["code"]
```
