New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@ui18n/selector-react

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ui18n/selector-react

🎨 Beautiful, accessible language selector component for React with auto-discovery, custom styling, and zero dependencies

next
latest
Source
npmnpm
Version
0.1.0-rc.2
Version published
Maintainers
1
Created
Source

@ui18n/selector-react

极简、可访问的语言选择下拉(React)。默认仅中/英,支持自动发现 /.ui18n/languages.json 扩展语言。无统计、无后端依赖,开放而轻量。

特性

  • 直接上手:仅一个 UI 组件 + 若干工具函数
  • 自动发现:可静默读取 /.ui18n/languages.json(数组或 {languages:[]})
  • A11y 友好:键盘导航、ARIA 标注
  • 受控/非受控均可,支持自定义渲染项
  • 零供应商锁定:只做语言入口,不绑定翻译后端
  • 可选“预览能力”:开发/演示态下可注入回调显示一句话的翻译预览(默认关闭,纯可选)

安装

  • Monorepo(本仓库)内 demo 通过 file: 依赖引入
  • 外部项目请使用包管理器安装(发布后):
    • npm i @ui18n/selector-react
    • peer: react/react-dom >= 18

快速上手 import { UI18nLanguageDropdown } from "@ui18n/selector-react";

function Example() { const [lang, setLang] = React.useState("en"); return ( <UI18nLanguageDropdown value={lang} onChange={setLang} // 默认仅 ["en","zh-CN"],可传入扩展列表 languages={["en","zh-CN","ja-JP"]} // 自动发现(默认 true);可关闭或自定义路径 autoDiscover discoverPath="/.ui18n/languages.json" placeholder="搜索语言…" className="w-full max-w-sm" /> ); }

自动发现协议

  • 支持两种返回格式:
    • ["en","zh-CN","ja-JP"]
    • { "languages": ["en","zh-CN","ja-JP"] }
  • fetch 失败或未发现时自动降级为默认中英

API

  • 组件 UI18nLanguageDropdown

    • value?: string 受控值
    • defaultValue?: string 非受控初始值(默认 "en")
    • onChange?: (lang: string) => void 变更回调
    • languages?: string[] 预置语言(默认 ["en","zh-CN"])
    • autoDiscover?: boolean 是否自动发现(默认 true)
    • discoverPath?: string 自动发现路径(默认 "/.ui18n/languages.json")
    • placeholder?: string | (ctx => string) 占位符(默认:系统语言本地化名称 + " - ui18n");可传函数以生成
    • className?: string 自定义类名
    • renderItem?: (lang: string, label: string) => React.ReactNode 自定义项渲染
    • showBrandSuffix?: boolean 是否显示品牌后缀(默认 true)
    • brandSuffix?: string 品牌后缀字符串(默认 "- ui18n")
    • showSearchBox?: boolean 是否显示内置搜索框(默认 true)
    • renderSearchInput?: (props) => React.ReactNode 自定义搜索输入完整渲染
    • filter?: (lang, query, meta) => boolean 自定义过滤逻辑
    • selectFirstMatchOnEnter?: boolean 回车时若无聚焦项选中首个匹配(默认 true)
    • notFoundText?: string | (query => string) 无匹配提示文案(默认“无法找到该语言,请重新输入”)
    • debounceMs?: number 输入去抖毫秒数(默认 0)
    • 预览(可选,仅开发/演示用)
      • previewText?: string 要预览的一句话(如 "Hello world")
      • onPreviewRequest?: (args: { text: string; to: string; from?: string }) => Promise
      • previewRender?: (state: { status: 'idle'|'loading'|'success'|'error'; result?: string; error?: unknown }) => React.ReactNode
  • Hooks/工具(按需引入)

    • useLanguageDropdown(options): 提供 headless 状态与 props 构造器
      • 返回:{ status, list, filtered, current, open, query, activeIndex, refs, setOpen, setQuery, setActiveIndex, commitSelect, onKeyDown, getTriggerProps, getSearchInputProps, getListboxProps, getOptionProps }
    • useDiscoveredLanguages({ autoDiscover, discoverPath, initial })
    • normalizeLocale(lang: string): string
    • labelForLang(lang: string): string

预览能力(可选)

  • 默认不显示。仅当同时提供 previewText 与 onPreviewRequest 时,下拉面板底部会出现一行“预览”区块。
  • 组件不会直接请求任何后端;仅调用你提供的 onPreviewRequest 回调。
  • 示例:通过你的后端代理 /api/translate 进行预览(前端不包含密钥)

function Demo() { async function preview({ text, to, from }: { text: string; to: string; from?: string }) { const res = await fetch("/api/translate", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text, to, from, model: "glm-4.5-flash" }), }); if (!res.ok) throw new Error(String(res.status)); const data = await res.json(); if (data?.ok) return String(data.data ?? ""); throw new Error(String(data?.error ?? "preview_failed")); }

return ( <UI18nLanguageDropdown value={"en"} onChange={() => {}} previewText="Hello world" onPreviewRequest={preview} /> ); }

样式

  • 默认极简样式(原生元素 + 少量 Tailwind 类)
  • 如果需要更精致的外观,可参考 demo 中的 shadcn 风格示例组件,自行组合 UI 库

可访问性

  • 触发按钮具备 aria-haspopup/expanded/controls
  • 列表使用 role=listbox / option,支持键盘上下/回车/ESC

建议集成方式

  • 词典/翻译来源由你的应用决定(本组件只提供语言入口)
  • 将所选语言与应用状态管理(如 context/store/URL)打通
  • 对大体量词典建议懒加载 + 预取 + 缓存

开源协议

  • MIT

最小集成片段

Vite(React + Vite)

// main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

ReactDOM.createRoot(document.getElementById("root")!).render(<App />);
// App.tsx
import * as React from "react";
import { UI18nLanguageDropdown } from "@ui18n/selector-react";

export default function App() {
  const [lang, setLang] = React.useState("en");
  return (
    <div style={{ padding: 16 }}>
      <UI18nLanguageDropdown
        value={lang}
        onChange={setLang}
        languages={["en", "zh-CN"]}
        autoDiscover
        discoverPath="/.ui18n/languages.json"
        placeholder="搜索语言…"
      />
    </div>
  );
}

Next.js(App Router)

// app/page.tsx (服务端)
// 推荐在 Client 组件中使用语言下拉
export default function Page() {
  return <ClientHome />;
}
// app/client-home.tsx
"use client";
import * as React from "react";
import { UI18nLanguageDropdown } from "@ui18n/selector-react";

export default function ClientHome() {
  const [lang, setLang] = React.useState("en");
  return (
    <UI18nLanguageDropdown
      value={lang}
      onChange={setLang}
      autoDiscover
      placeholder="搜索语言…"
    />
  );
}

Create React App

// App.tsx
import * as React from "react";
import { UI18nLanguageDropdown } from "@ui18n/selector-react";

function App() {
  const [lang, setLang] = React.useState("en");
  return (
    <UI18nLanguageDropdown
      value={lang}
      onChange={setLang}
      languages={["en", "zh-CN", "ja-JP"]}
    />
  );
}
export default App;

A11y 键盘导航清单

  • 触发区(按钮)
    • Enter / Space:打开下拉
    • Escape:关闭下拉并聚焦回按钮
    • ArrowDown(在触发上):打开并聚焦首项
  • 列表(role="listbox")
    • ArrowUp / ArrowDown:在选项间移动
    • Home / End:跳转到首/尾项
    • Enter / Space:选择当前聚焦项
    • Escape:关闭下拉
  • ARIA 语义
    • 触发:aria-haspopup="listbox"、aria-expanded、aria-controls
    • 列表:role="listbox";选项:role="option"、aria-selected
    • 活跃项:aria-activedescendant(或 roving tabindex)
  • 屏幕阅读器
    • 提供可读标签:aria-label 或与可见文本关联
    • 状态变化(选择、错误)可用 aria-live(若在预览区块中提示)
  • 焦点可见性
    • 明确的 focus 样式(轮廓或阴影),Tab 路径完整、可回退

Keywords

react

FAQs

Package last updated on 07 Sep 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts