flagcn
blocklanguage-pickersource: ok · refreshed
Language Picker
A searchable language picker with native names and RTL metadata.
npx shadcn add "https://flagcn.dev/r/language-picker.json"free
No selection tags yet — empty means no evidence, not lack of support.
Dependencies
Bundle weight · light@tabler/icons-react
Registry dependencies
buttoninputbadge
Files3 · source cached 10/7/2026, 3:09:24 PM
src/components/flags/language-picker.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
// 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
// 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"]