LanguagePicker
A drop-down of languages each written in its own name, sending the ISO 639-1 code with a form.
Demo
Each language is written in itself.
The site is shown in 日本語 (ja).
Get it
It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.
$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/language_picker.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/language_picker.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/language_picker.qwe
component LanguagePickerDemoLive() { state code = "ja" <div.demo-stack> <LanguagePicker name="ui_lang" label="Show the site in" value={code} field="language-picker-demo-live" only={["en", "fr", "de", "es", "pt", "ja", "zh", "ar"]} on_change={(c) => { code = c }}/> <p.demo-note>The site is shown in {language_picker_name(code)} ({code}).</p> </div>}view LanguagePickerDemo() = <div.demo-stack.demo-narrow> <LanguagePicker name="lang" label="Language" value="pt" field="language-picker-demo-all" favourites={["en", "pt"]} hint="Each language is written in itself." required={true}/> <LanguagePickerDemoLive/> <div.demo-row> <LanguagePicker name="lang_en" label="With English names" size="sm" english={true} placeholder="Choose one" field="language-picker-demo-english" only={["de", "ja", "ko", "en"]}/> <LanguagePicker name="locked" label="Disabled" size="sm" value="fr" only={["fr"]} disabled={true}/> </div> </div>Source
ui/language_picker.qwe
// LanguagePicker: a drop-down of languages, each written in its own name// ("Deutsch", "日本語"), so anyone finds theirs whatever the page's language. It is// the browser's own <select>, so a plain <form> sends the ISO 639-1 code ("de")// as `name`, and it works with the keyboard.//// <LanguagePicker name="lang" label="Language" value="pt"/>// <LanguagePicker name="ui_lang" label="Show the site in" only={["en", "fr", "de", "es"]}/>// <LanguagePicker name="lang" label="Language" english={true} favourites={["en"]}/>//// value: the code chosen to start with. only: the codes to offer, in place of the// built-in list of about seventy (`language_picker_languages`, a list of// [code, own name, English name]). favourites: codes listed first, under "Common".// english (false): also the English name after the own one ("Deutsch (German)").// placeholder: a first line that cannot be chosen. hint, error, required,// disabled, field (the id, default "language-picker-" + name) and size ("sm" or// "md") as in Select. Each option carries lang="<code>", so screen readers say// the name in its language.// on_change(code): inside one of your components, given the code chosen.import ui/theme.qwe use ui_clslet language_picker_css = css`.ui-language-picker { display: grid; gap: 6px; min-width: 0; font: 14px/1.4 var(--ui-font); color: var(--ui-fg); }.ui-language-picker-label { font-size: 13px; font-weight: 500; color: var(--ui-fg); }.ui-language-picker-req { margin-left: 2px; color: var(--ui-danger); }.ui-language-picker-box { position: relative; display: block; }.ui-language-picker-box::after { content: ""; position: absolute; right: 14px; top: 50%; width: 7px; height: 7px; margin-top: -6px; border: solid var(--ui-muted); border-width: 0 1.5px 1.5px 0; transform: rotate(45deg); pointer-events: none;}.ui-language-picker select { appearance: none; display: block; width: 100%; height: 38px; padding: 0 36px 0 12px; background: var(--ui-surface); color: var(--ui-fg); font: 14px var(--ui-font); border: 1px solid var(--ui-border); border-radius: var(--ui-radius-sm); cursor: pointer; text-overflow: ellipsis; transition: border-color .15s, box-shadow .15s;}.ui-language-picker select:hover { border-color: var(--ui-strong); }.ui-language-picker select:focus-visible { outline: none; border-color: var(--ui-accent); box-shadow: var(--ui-focus); }.ui-language-picker select:has(option[value=""]:checked) { color: var(--ui-muted); }.ui-language-picker option { color: var(--ui-fg); background: var(--ui-surface); }.ui-language-picker-sm select { height: 30px; padding: 0 30px 0 10px; font-size: 13px; }.ui-language-picker-sm .ui-language-picker-box::after { right: 11px; width: 6px; height: 6px; margin-top: -5px; }.ui-language-picker-invalid select, .ui-language-picker-invalid select:hover { border-color: var(--ui-danger); }.ui-language-picker-hint, .ui-language-picker-error { margin: 0; font-size: 13px; color: var(--ui-muted); }.ui-language-picker-error { color: var(--ui-danger); }.ui-language-picker-disabled { opacity: .55; }.ui-language-picker-disabled select { cursor: not-allowed; }@media (prefers-reduced-motion: reduce) { .ui-language-picker select { transition: none; } }`// [ISO 639-1 code, the language's own name, its English name], in the order of the English names.let language_picker_languages = [ ["af", "Afrikaans", "Afrikaans"], ["sq", "Shqip", "Albanian"], ["am", "አማርኛ", "Amharic"], ["ar", "العربية", "Arabic"], ["hy", "Հայերեն", "Armenian"], ["az", "Azərbaycan dili", "Azerbaijani"], ["eu", "Euskara", "Basque"], ["be", "Беларуская", "Belarusian"], ["bn", "বাংলা", "Bengali"], ["bs", "Bosanski", "Bosnian"], ["bg", "Български", "Bulgarian"], ["my", "မြန်မာဘာသာ", "Burmese"], ["ca", "Català", "Catalan"], ["zh", "中文", "Chinese"], ["hr", "Hrvatski", "Croatian"], ["cs", "Čeština", "Czech"], ["da", "Dansk", "Danish"], ["nl", "Nederlands", "Dutch"], ["en", "English", "English"], ["et", "Eesti", "Estonian"], ["fi", "Suomi", "Finnish"], ["fr", "Français", "French"], ["gl", "Galego", "Galician"], ["ka", "ქართული", "Georgian"], ["de", "Deutsch", "German"], ["el", "Ελληνικά", "Greek"], ["gu", "ગુજરાતી", "Gujarati"], ["he", "עברית", "Hebrew"], ["hi", "हिन्दी", "Hindi"], ["hu", "Magyar", "Hungarian"], ["is", "Íslenska", "Icelandic"], ["id", "Bahasa Indonesia", "Indonesian"], ["ga", "Gaeilge", "Irish"], ["it", "Italiano", "Italian"], ["ja", "日本語", "Japanese"], ["kn", "ಕನ್ನಡ", "Kannada"], ["kk", "Қазақ тілі", "Kazakh"], ["km", "ខ្មែរ", "Khmer"], ["ko", "한국어", "Korean"], ["lo", "ລາວ", "Lao"], ["lv", "Latviešu", "Latvian"], ["lt", "Lietuvių", "Lithuanian"], ["mk", "Македонски", "Macedonian"], ["ms", "Bahasa Melayu", "Malay"], ["ml", "മലയാളം", "Malayalam"], ["mt", "Malti", "Maltese"], ["mr", "मराठी", "Marathi"], ["mn", "Монгол", "Mongolian"], ["ne", "नेपाली", "Nepali"], ["nb", "Norsk bokmål", "Norwegian"], ["fa", "فارسی", "Persian"], ["pl", "Polski", "Polish"], ["pt", "Português", "Portuguese"], ["pa", "ਪੰਜਾਬੀ", "Punjabi"], ["ro", "Română", "Romanian"], ["ru", "Русский", "Russian"], ["sr", "Српски", "Serbian"], ["si", "සිංහල", "Sinhala"], ["sk", "Slovenčina", "Slovak"], ["sl", "Slovenščina", "Slovenian"], ["es", "Español", "Spanish"], ["sw", "Kiswahili", "Swahili"], ["sv", "Svenska", "Swedish"], ["tl", "Tagalog", "Tagalog"], ["ta", "தமிழ்", "Tamil"], ["te", "తెలుగు", "Telugu"], ["th", "ไทย", "Thai"], ["tr", "Türkçe", "Turkish"], ["uk", "Українська", "Ukrainian"], ["ur", "اردو", "Urdu"], ["uz", "Oʻzbekcha", "Uzbek"], ["vi", "Tiếng Việt", "Vietnamese"], ["cy", "Cymraeg", "Welsh"],]// A language's own name ("Deutsch" for "de"), or the code when it is not known.fn language_picker_name(code: str) -> str { let c = lower(code) let hit = find(language_picker_languages, (l) => l[0] == c) hit != null ? hit[1] : code}view LanguagePicker(name: str? = null, label: str? = null, value: str? = null, only: list? = null, favourites: list = [], english: bool = false, placeholder: str? = null, hint: str? = null, error: str? = null, required: bool = false, disabled: bool = false, field: str? = null, size: str = "md", on_change = null, class: str? = null) { style global language_picker_css let id = field ?? `language-picker-${name ?? "language"}` let described = ui_cls([hint != null && `${id}-hint`, error != null && `${id}-error`]) let chosen = value != null ? lower(value) : null let wanted = only != null ? only | map((c) => lower(c)) : null let all = language_picker_languages | filter((l) => wanted == null || contains(wanted, l[0])) let favs = favourites | map((c) => lower(c)) let top = favs | map((c) => find(all, (l) => l[0] == c)) | filter((l) => l != null) let rest = len(top) > 0 ? all | filter((l) => !contains(favs, l[0])) : all let opt = (l) => <option value={l[0]} lang={l[0]} selected={l[0] == chosen}> {english && l[1] != l[2] ? `${l[1]} (${l[2]})` : l[1]} </option> <div class={ui_cls(["ui-language-picker", size == "sm" && "ui-language-picker-sm", error != null && "ui-language-picker-invalid", disabled && "ui-language-picker-disabled", class])}> {label != null && <label.ui-language-picker-label for={id}> {label}{required && <span.ui-language-picker-req aria-hidden="true">*</span>} </label>} <span.ui-language-picker-box> <select id={id} name={name} required={required} disabled={disabled} autocomplete="language" aria-invalid={error != null ? "true" : null} aria-describedby={described != "" ? described : null} @change={(e) => { on_change && on_change(e.value) }}> {placeholder != null && <option value="" disabled selected={chosen == null}>{placeholder}</option>} {len(top) > 0 ? [<optgroup label="Common">{for l in top => opt(l)}</optgroup>, <optgroup label="All languages">{for l in rest => opt(l)}</optgroup>] : (for l in rest => opt(l))} </select> </span> {hint != null && <p.ui-language-picker-hint id={`${id}-hint`}>{hint}</p>} {error != null && <p.ui-language-picker-error id={`${id}-error`} role="alert">{error}</p>} </div>}Related
BirthdayInputDay, month and year drop-downs that give one date in a hidden input, the days following the month and the years the age allowed.5 views · 7 downloadsCountryPickerA drop-down of every country with its flag and ISO code, favourites first, sending the two-letter code with a form.5 views · 7 downloadsFontPickerA drop-down of fonts, each option drawn in itself, with a preview line in the one chosen.5 views · 7 downloadsMultiSelectSeveral choices from a searchable drop-down of checkboxes, shown as chips that come off with ×.5 views · 7 downloadsTimezonePickerA drop-down of time zones grouped by region, each with its UTC offset for today, sending the IANA name with a form.5 views · 5 downloadsCheckboxCardsAny number of choices as cards with a title, a line of detail and a check, each a real checkbox a form sends.5 views · 6 downloads
Thread
Thread 0 posts
On LanguagePicker: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.