npm package: @mico_fe/i18n-react
Description: React 18 i18n runtime - auto internationalization
Author: manwang
Latest version: 1.0.0

# @mico_fe/i18n-react

> React 18 国际化运行时 - 自动国际化解决方案

## 📦 安装

```bash
npm install @mico_fe/i18n-react
# 或
pnpm add @mico_fe/i18n-react
# 或
yarn add @mico_fe/i18n-react
```

**配合编译时插件使用（推荐）：**

```bash
# Vite 项目
npm install @mico_fe/vite-plugin-i18n-react -D

# Webpack 项目
npm install babel-plugin-auto-i18n -D
```

## 🚀 快速开始

### 1. 创建语言包

```
src/locales/
├── zh-CN.json
├── en-US.json
└── index.ts
```

**zh-CN.json**
```json
{
  "button.submit": "提交",
  "button.cancel": "取消",
  "message.welcome": "欢迎使用",
  "message.greeting": "你好，{name}！",
  "message.items": "共 {count} 条数据"
}
```

**en-US.json**
```json
{
  "button.submit": "Submit",
  "button.cancel": "Cancel",
  "message.welcome": "Welcome",
  "message.greeting": "Hello, {name}!",
  "message.items": "Total {count} items"
}
```

**index.ts**
```ts
import zhCN from './zh-CN.json'
import enUS from './en-US.json'
import type { ReactLocaleModule } from '@mico_fe/i18n-react'

// 语言包数据
export const messages = {
  'zh-CN': zhCN,
  'en-US': enUS,
} as Record<string, Record<string, string>>

// 可用语言列表
export const availableLangs = Object.keys(messages)

// 默认语言
export const defaultLang = 'zh-CN'

// 获取初始语言
export function getInitialLang(): string {
  if (typeof window === 'undefined') return defaultLang
  
  const stored = localStorage.getItem('lang')
  if (stored && availableLangs.includes(stored)) {
    return stored
  }
  
  const browserLang = navigator.language
  if (availableLangs.includes(browserLang)) {
    return browserLang
  }
  
  return defaultLang
}

// 语言变化回调（可选）
export function onLangChange(lang: string): void {
  localStorage.setItem('lang', lang)
  document.documentElement.lang = lang
  console.log('[i18n] Language changed to:', lang)
}

// 可选：自定义参数替换
// export function replaceParams(text: string, params: Record<string, any>): string {
//   return text.replace(/\{\{(\w+)\}\}/g, (_, key) => params[key] ?? _)
// }

// 可选：自定义语言映射
// export const langMap = {
//   'ja-JP': { name: '日本語', flag: '🇯🇵' }
// }

// 导出模块
const localeModule: ReactLocaleModule = {
  messages,
  availableLangs,
  defaultLang,
  getInitialLang,
  onLangChange,
}

export default localeModule
```

### 2. 初始化

```tsx
// main.tsx
import { createRoot } from 'react-dom/client'
import { I18nProvider } from '@mico_fe/i18n-react'
import localeModule from './locales'
import App from './App'

createRoot(document.getElementById('root')!).render(
  <I18nProvider 
    localeModule={localeModule}
    fallbackLang="zh-CN"
    warnOnMissing={true}
  >
    <App />
  </I18nProvider>
)
```

### 3. 在组件中使用

```tsx
import { useTranslation } from '@mico_fe/i18n-react'

function App() {
  const { t, tt, te, tte, lang, setLang } = useTranslation()
  
  return (
    <div>
      {/* 基本用法 */}
      <button>{t('button.submit')}</button>
      
      {/* 带参数 */}
      <p>{t('message.greeting', { name: 'React' })}</p>
      
      {/* 按文案翻译 */}
      <p>{tt('欢迎使用')}</p>
      
      {/* 直接写中文（配合编译时插件自动转换） */}
      <p>欢迎使用</p>
      
      {/* 切换语言 */}
      <button onClick={() => setLang('en-US')}>
        Switch to English
      </button>
      
      {/* 当前语言 */}
      <span>Current: {lang}</span>
    </div>
  )
}
```

## 📖 API

### I18nProvider

Provider 组件：

```tsx
<I18nProvider
  localeModule={localeModule}  // 语言模块（必需）
  fallbackLang="zh-CN"         // 回退语言
  warnOnMissing={true}         // 缺少翻译时警告
  onLangChange={(lang) => {}}  // 语言变化回调
>
  <App />
</I18nProvider>
```

### useTranslation()

主 Hook，返回所有翻译相关功能：

```ts
const {
  // 翻译函数
  t,    // (key: string, params?: object) => string
  tt,   // (text: string, params?: object) => string
  te,   // (key: string, lang?: string) => boolean
  tte,  // (text: string) => boolean
  
  // 语言状态
  lang,      // string - 当前语言代码
  langs,     // string[] - 可用语言列表
  langName,  // string - 当前语言名称
  langFlag,  // string - 当前语言旗帜
  
  // 语言操作
  setLang,      // (lang: string) => void
  getLangName,  // (code: string) => string
  getLangFlag,  // (code: string) => string
} = useTranslation()
```

### useT() / useTT() / useLang()

独立的 Hooks：

```ts
// 只获取 t 函数
const t = useT()
t('button.submit')

// 只获取 tt 函数
const tt = useTT()
tt('提交')

// 只获取语言状态
const { lang, langs, setLang } = useLang()
```

### t(key, params?)

按 key 翻译：

```ts
t('button.submit')  // "提交"
t('message.greeting', { name: 'React' })  // "你好，React！"
t('message.items', { count: 100 })  // "共 100 条数据"
```

### tt(text, params?)

按文案翻译（运行时根据中文查找 key）：

```ts
tt('提交')  // 中文环境: "提交" / 英文环境: "Submit"
tt('你好，{name}！', { name: 'React' })  // "Hello, React!"
```

### te(key, lang?) / tte(text)

检查翻译是否存在：

```ts
te('button.submit')  // true
te('not.exist')      // false
tte('提交')          // true
tte('不存在的文案')    // false
```

## 🎨 内置组件

### LocaleSwitcher

下拉选择式语言切换器：

```tsx
import { LocaleSwitcher } from '@mico_fe/i18n-react'

function Header() {
  return (
    <LocaleSwitcher 
      placement="bottom-end"
      onChange={(lang) => console.log('Changed to:', lang)}
    />
  )
}
```

**Props：**

| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `className` | `string` | `''` | 自定义 className |
| `placement` | `string` | `'bottom-end'` | 下拉框位置 |
| `onChange` | `(lang: string) => void` | - | 语言变化回调 |

### FloatingLocaleSwitcher

浮动可拖动语言切换器：

```tsx
import { FloatingLocaleSwitcher } from '@mico_fe/i18n-react'

function App() {
  return (
    <>
      <FloatingLocaleSwitcher 
        initialX={20}
        initialY={100}
        storageKey="my-locale-pos"
        onChange={(lang) => console.log('Changed to:', lang)}
      />
      {/* 其他内容 */}
    </>
  )
}
```

**Props：**

| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `initialX` | `number` | `20` | 初始 X 位置 |
| `initialY` | `number` | `100` | 初始 Y 位置 |
| `storageKey` | `string` | `'floating-locale-position'` | localStorage 存储 key |
| `onChange` | `(lang: string) => void` | - | 语言变化回调 |

**特性：**
- 🖱️ 可拖动定位
- 💾 位置自动保存到 localStorage
- 📱 支持触摸设备
- 🎯 边界检测，不会拖出屏幕

## 📝 TypeScript

### ReactLocaleModule 接口

```ts
interface ReactLocaleModule {
  // 必需
  messages: Record<string, Record<string, string>>
  availableLangs: string[]
  defaultLang: string
  getInitialLang: () => string
  
  // 可选
  onLangChange?: (lang: string) => void
  langMap?: Record<string, LangInfo>
  replaceParams?: (text: string, params: Record<string, any>) => string
}
```

### I18nContextValue 接口

```ts
interface I18nContextValue {
  lang: string
  langs: string[]
  langName: string
  langFlag: string
  setLang: (lang: string) => void
  t: (key: string, params?: Record<string, any>) => string
  tt: (text: string, params?: Record<string, any>) => string
  te: (key: string, lang?: string) => boolean
  tte: (text: string) => boolean
  getLangName: (lang: string) => string
  getLangFlag: (lang: string) => string
}
```

## 💡 最佳实践

### 1. 配合 useMemo 使用

```tsx
function Component() {
  const { t, tt, lang } = useTranslation()
  
  // 语言变化时重新计算
  const welcomeText = useMemo(() => tt('欢迎使用'), [lang])
  const greeting = useMemo(() => t('message.greeting', { name: 'React' }), [lang])
  
  return <div>{welcomeText} - {greeting}</div>
}
```

### 2. 在组件外使用

如果需要在组件外使用翻译（如工具函数），建议传入翻译函数：

```ts
// utils.ts
export function formatMessage(t: (key: string) => string) {
  return t('message.welcome')
}

// Component.tsx
const { t } = useTranslation()
const msg = formatMessage(t)
```

## 📄 License

MIT