Skip to content

Replace language tag text inputs with searchable BCP 47 locale picker #97

@JohnRDOrazio

Description

@JohnRDOrazio

Problem

Language tags in the ontology editor (annotations on classes, properties, individuals) are currently free-text <input> fields. Users can type any arbitrary string, which risks invalid or inconsistent language tags in the ontology (e.g. english instead of en, or en_US instead of en-US). Since language tags must conform to BCP 47, we should constrain input to valid codes only.

Affected Components

Component File Current UI
AnnotationRow components/editor/standard/AnnotationRow.tsx <input type="text"> (w-14)
InlineAnnotationAdder components/editor/standard/InlineAnnotationAdder.tsx <input type="text"> (w-14)

These are used in ClassDetailPanel, PropertyDetailPanel, IndividualDetailPanel, and AnnotationEditor for editing rdfs:label, rdfs:comment, skos:definition, and custom annotation properties.

Proposed Solution: Searchable Combobox

Since BCP 47 defines hundreds of valid language codes, a plain <select> dropdown would be unwieldy. Instead, implement a searchable combobox (typeahead select) pattern:

UI Behavior

  1. Compact trigger — displays the current language code (e.g. en) plus flag emoji, same width as the current input (~w-14)
  2. Click/focus opens a popover with a search input and scrollable list of language codes
  3. Search filters by code (en, fr-CA) and by display name (English, French (Canada))
  4. Selection closes the popover and updates the language tag
  5. Keyboard — arrow keys navigate, Enter selects, Escape closes, typing filters

Implementation Plan

1. Add dependencies

  • cmdk (Command Menu) — lightweight, composable, accessible combobox primitive
  • @radix-ui/react-popover — already using Radix UI in the project

2. Create shared LanguagePicker component

components/editor/LanguagePicker.tsx — a self-contained combobox that:

  • Accepts value: string and onChange: (code: string) => void
  • Renders the current code + flag emoji as a compact button trigger
  • Opens a Popover with a cmdk Command list of language options
  • Supports keyboard navigation and search filtering

3. Create language code data module

lib/i18n/languageCodes.ts — curated list of BCP 47 codes with metadata:

export interface LanguageOption {
  code: string;       // BCP 47 code, e.g. "en", "fr-CA"
  name: string;       // English name, e.g. "English", "French (Canada)"
  nativeName: string; // Native name, e.g. "English", "Français (Canada)"
}
  • Start with ~80-100 most commonly used language codes (ISO 639-1 base languages + major regional variants)
  • Organize with common languages (en, fr, de, es, it, pt, la, etc.) pinned to the top as "frequently used"
  • Can be expanded over time; the combobox search makes a large list manageable

4. Replace text inputs

  • In AnnotationRow.tsx: replace the <input type="text"> (lines 70-78) with <LanguagePicker>
  • In InlineAnnotationAdder.tsx: replace the <input type="text"> (lines 203-211) with <LanguagePicker>

5. Consolidate LANG_TO_COUNTRY mapping

The existing LANG_TO_COUNTRY map in lib/utils.ts (lines 107-145) and langToFlag() can be consolidated with the new language code data module to avoid duplication.

Wireframe

┌──────────────────────────────────────────────────┐
│  rdfs:label   [Hello World        ] 🇺🇸 [en ▾]  │  ← compact trigger
└──────────────────────────────────────────────────┘
                                          │
                                    ┌─────▼──────────────┐
                                    │ 🔍 Search...       │
                                    ├────────────────────┤
                                    │ Frequently used     │
                                    │  🇺🇸 en   English   │
                                    │  🇫🇷 fr   French    │
                                    │  🇩🇪 de   German    │
                                    │  🇪🇸 es   Spanish   │
                                    │  🇮🇹 it   Italian   │
                                    │  🇻🇦 la   Latin     │
                                    │ ─────────────────── │
                                    │ All languages       │
                                    │  🇸🇦 ar   Arabic    │
                                    │  🇧🇬 bg   Bulgarian │
                                    │  🇨🇳 zh   Chinese   │
                                    │  ...                │
                                    └────────────────────┘

Acceptance Criteria

  • Language tags can only be set to valid BCP 47 codes (no free-text entry)
  • Combobox is searchable by code and display name (English and native)
  • Keyboard accessible (arrow keys, Enter, Escape, type-to-filter)
  • Flag emoji displayed alongside the code (using existing langToFlag() logic)
  • Compact trigger fits in the same layout space as the current text input
  • Frequently-used languages pinned to top of the list
  • Default language remains en for new annotations
  • Existing language tags in ontologies are preserved (no data migration needed)
  • Dark mode support consistent with existing editor styling

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions