@yyc3/i18n-core
v2.4.0
Published
ð YYC³ Production-Ready Internationalization (i18n) Framework - High-performance, plugin-based, zero-dependency i18n solution for TypeScript/JavaScript applications
Maintainers
Keywords
Readme
@yyc3/i18n-core
ð YYC³ Production-Ready Internationalization Framework
髿§èœãæä»¶åãé¶äŸèµç i18n è§£å³æ¹æ¡ïŒäžäžºç°ä»£ Web åºçšè®Ÿè®¡
ð ç®åœ
- ç¹æ§æŠè§
- 䞺ä»ä¹éæ© @yyc3/i18n-core
- å®è£ æå
- å¿«éåŒå§
- æ¶æè®Ÿè®¡
- æ žå¿æš¡å诊解
- åè·¯åŸå¯Œå ¥ïŒTree ShakingïŒ
- API åè
- é«çº§åèœ
- æäœ³å®è·µ
- æµè¯èŠç
- æ§èœåºå
- å®å šç¹æ§
- åžžè§é®é¢
- è¿ç§»æå
- èŽ¡ç®æå
- License
âš ç¹æ§æŠè§
ð¯ æ žå¿äŒå¿
| ç¹æ§ | æè¿° | ç¶æ | |------|------|------| | é¶äŸèµè¿è¡æ¶ | æ ä»»äœç产äŸèµïŒçº¯ TypeScript å®ç° | â | | 10 ç§è¯èšå 眮 | en/zh-CN/zh-TW/ja/ko/fr/de/es/pt-BR/ar(RTL) | â | | AI ç¿»è¯éæ | OpenAI + Ollama æ¬å°æš¡åæ¯æ | â | | MCP åè®®åç | AI Agent å·¥å ·éæïŒ7 䞪 i18n å·¥å · | â | | ICU MessageFormat | 宿Žå®ç°ïŒ12 ç§è¯æ³ç±»å | â | | RTL åçæ¯æ | é¿æäŒ¯è¯ç RTL è¯èšåžå±èªåšå€ç | â | | LRU çŒåç³»ç» | <0.1ms çŒååœäžååºæ¶éŽ | â | | æä»¶åæ¶æ | 坿©å±ççåœåšæé©åç³»ç» | â | | äŒäžçº§å®å š | OWASP L4 å®å šæ å | â |
ð äžäž»æµæ¹æ¡å¯¹æ¯
| ç¹æ§ | @yyc3/i18n-core | react-i18next | vue-i18n | typesafe-i18n | |------|:---:|:---:|:---:|:---:| | äŸèµæ°é | 0 | 3+ | 2+ | 0 | | æå å€§å° | ~15KB | ~33KB | ~1.5MB | ~1KB | | AI ç¿»è¯ | â å 眮 | æä»¶ | æä»¶ | â | | MCP åè®® | â å 眮 | â | â | â | | ICU MessageFormat | â å®æŽ | æä»¶ | åºç¡ | åºç¡ | | RTL æ¯æ | â åç | é 眮 | é 眮 | â | | å®å šç级 | OWASP L4 | åºç¡ | åºç¡ | â | | äžæäŒå | 10 è¯èš | é 眮 | é 眮 | é 眮 |
ð€ 䞺ä»ä¹éæ© @yyc3/i18n-core?
1ïžâ£ çæ£çé¶äŸèµ
npm install @yyc3/i18n-core
# ä»
æ€äžäžªå
ïŒæ å
¶ä»äŸèµ2ïžâ£ åŒç®±å³çšçäžææ¯æ
import { i18n, t } from '@yyc3/i18n-core';
// 10 ç§è¯èšå³æ¶å¯çš
await i18n.setLocale('zh-CN'); // ç®äœäžæ
await i18n.setLocale('zh-TW'); // ç¹äœäžæ
await i18n.setLocale('ja'); // æ¥è¯
await i18n.setLocale('ko'); // é©è¯
// ... æŽå€3ïžâ£ AI èµèœç¿»è¯
import { AIProviderManager, OpenAIProvider } from '@yyc3/i18n-core/ai';
const ai = new AIProviderManager();
ai.register(new OpenAIProvider({ apiKey: 'your-key' }));
const result = await ai.translate({
sourceText: 'Hello World',
sourceLocale: 'en',
targetLocale: 'zh-CN',
});
console.log(result.translatedText); // "äœ å¥œäžç"4ïžâ£ MCP åè®®åçéæ
import { MCPServer, registerI18nTools, StdioTransport } from '@yyc3/i18n-core/mcp';
const server = new MCPServer({
name: 'i18n-tools',
version: '1.4.0',
transport: new StdioTransport(),
});
registerI18nTools(server);
await server.start();
// ç°åš Claude/Cursor ç AI å·¥å
·å¯ä»¥äœ¿çš i18n åèœäºïŒðŠ å®è£ æå
åçœ®èŠæ±
- Node.js >= 16.0.0
- TypeScript >= 5.0 (æšèïŒäœä¹æ¯æ JavaScript)
å®è£ åœä»€
# äœ¿çš npm
npm install @yyc3/i18n-core
# äœ¿çš pnpm (æšè)
pnpm add @yyc3/i18n-core
# äœ¿çš yarn
yarn add @yyc3/i18n-coreéªè¯å®è£
import { i18n, t } from '@yyc3/i18n-core';
console.log(t('common.welcome')); // "Welcome"
console.log('â
@yyc3/i18n-core å®è£
æå');ð å¿«éåŒå§
1. æç®äœ¿çš â é¶é 眮å¯åš
import { i18n, t } from '@yyc3/i18n-core';
// çŽæ¥äœ¿çšïŒæ éé
眮
t('common.welcome'); // "Welcome"
// 忢è¯èš
await i18n.setLocale('zh-CN');
t('common.welcome'); // "欢è¿"2. 垊æä»¶ç宿Žé 眮
import { i18n, t, I18nEngine } from '@yyc3/i18n-core';
import {
createConsoleLogger,
MissingKeyReporter,
PerformanceTracker,
} from '@yyc3/i18n-core/plugins';
// åå»ºåŒæå®äŸ
const engine = new I18nEngine({
defaultLocale: 'en',
fallbackLocale: 'en',
cache: {
maxSize: 1000,
ttl: 3600000, // 1å°æ¶
},
});
// 泚åæä»¶
engine.plugins.register(createConsoleLogger());
engine.plugins.register(new MissingKeyReporter().createPlugin());
engine.plugins.register(
new PerformanceTracker({ slowThreshold: 10 }).createPlugin()
);
// åå§å
await engine.init();
// 䜿çš
engine.t('greeting', { name: 'World' }); // "Hello, World!"3. React 项ç®éæ
// App.tsx
import { useEffect, useState } from 'react';
import { i18n, t } from '@yyc3/i18n-core';
function App() {
const [locale, setLocale] = useState('en');
useEffect(() => {
i18n.setLocale(locale);
}, [locale]);
return (
<div>
<h1>{t('app.title')}</h1>
<p>{t('greeting', { name: 'User' })}</p>
<select value={locale} onChange={(e) => setLocale(e.target.value)}>
<option value="en">English</option>
<option value="zh-CN">äžæ</option>
<option value="ja">æ¥æ¬èª</option>
</select>
</div>
);
}4. Vue 项ç®éæ
<template>
<div>
<h1>{{ $t('app.title') }}</h1>
<button @click="changeLocale('zh-CN')">äžæ</button>
<button @click="changeLocale('en')">English</button>
</div>
</template>
<script setup lang="ts">
import { i18n, t } from '@yyc3/i18n-core';
import { ref } from 'vue';
const changeLocale = async (locale: string) => {
await i18n.setLocale(locale);
};
</script>5. Node.js / å端䜿çš
// server.ts
import { I18nEngine } from '@yyc3/i18n-core';
const i18n = new I18nEngine({
defaultLocale: 'zh-CN',
});
export function middleware(req, res, next) {
const locale = req.headers['accept-language']?.split(',')[0] || 'zh-CN';
i18n.setLocale(locale);
req.t = (key, params?) => i18n.t(key, params);
next();
}
// åšè·¯ç±äžäœ¿çš
app.get('/api/hello', (req, res) => {
res.json({ message: req.t('hello') });
});ðïž æ¶æè®Ÿè®¡
å屿¶æ
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â Application Layer â
â (React / Vue / Angular / Node.js) â
âââââââââââââââââââââââââ¬ââââââââââââââââââââââââââââââââââââââ
â
âââââââââââââââââââââââââŒââââââââââââââââââââââââââââââââââââââ
â Public API Layer â
â i18n / t / I18nEngine / PluginManager â
âââââââââââââââââââââââââ¬ââââââââââââââââââââââââââââââââââââââ
â
âââââââââââââââââŒââââââââââââââââ¬ââââââââââââââââ
⌠⌠⌠âŒ
âââââââââââââ âââââââââââââ âââââââââââââ âââââââââââââ
â Core â â Cache â â Plugins â â Formats â
â Engine â â LRU â â System â â ICU/MF â
âââââââ¬ââââââ âââââââ¬ââââââ âââââââ¬ââââââ âââââââ¬ââââââ
â â â â
âââââââââââââââââŽââââââââââââââââŽââââââââââââââââ
â
âââââââââââââââââŒââââââââââââââââ¬ââââââââââââââââ
⌠⌠⌠âŒ
âââââââââââââ âââââââââââââ âââââââââââââ âââââââââââââ
â AI â â MCP â â Security â â RTL â
â Translationâ â Server â â OWASP L4 â â Utils â
âââââââââââââ âââââââââââââ âââââââââââââ âââââââââââââæš¡åè莣
| æš¡å | æä»¶è·¯åŸ | è莣 | å€æåºŠ |
|------|----------|------|--------|
| Core Engine | lib/engine.ts | ç¿»è¯åŒæãè¯èšåæ¢ãåæ°æåŒ | âââ |
| Cache System | lib/cache.ts | LRU çŒåãTTL è¿æãç»è®¡ | ââ |
| Plugin System | lib/plugins.ts | çåœåšæé©åãäºä»¶è®¢é
| ââ |
| ICU Engine | lib/icu/* | MessageFormat è§£æäžçŒè¯ | âââ |
| AI Translation | lib/ai/* | LLM ç¿»è¯ã莚éè¯äŒ° | âââ |
| MCP Server | lib/mcp/* | åè®®å®ç°ãå·¥å
·æ³šå | âââ |
| Security | lib/security/* | ReDoS鲿€ã泚å
¥æ£æµ | ââ |
| RTL Utils | lib/rtl-utils.ts | åžå±éåãæ¹åæ£æµ | â |
æ°æ®æµ
çšæ·è°çš t(key, params)
â
Plugin.beforeTranslate()
â
Cache Lookup â Hit? â Return Cached Result
â Miss
ICU Parser â Parse Syntax Tree
â
Formatter â Interpolate Parameters
â
Plugin.afterTranslate()
â
Cache.Store(Result)
â
Return Translated StringðŠ æ žå¿æš¡å诊解
1. Core Engine (@yyc3/i18n-core)
ç¿»è¯åŒæçæ žå¿ïŒæäŸå®æŽç i18n èœåã
import { I18nEngine, i18n, t } from '@yyc3/i18n-core';
// æ¹åŒ1: 䜿çšå
šå±åäŸ (æšèç®ååºæ¯)
t('welcome'); // "Welcome"
t('greeting', { name: 'World' }); // "Hello, World!"
// æ¹åŒ2: å建ç¬ç«å®äŸ (æšèå€æåºæ¯)
const engine = new I18nEngine({
defaultLocale: 'en',
fallbackLocale: 'en',
debug: false,
});
await engine.init();
// 忢è¯èš
await engine.setLocale('zh-CN');
// æ¹éç¿»è¯
const results = await engine.batchTranslate(['key1', 'key2', 'key3']);
// è·åç»è®¡ä¿¡æ¯
const stats = engine.getStats();
console.log(stats.cacheHits, stats.cacheMisses);
// 订é
è¯èšåæŽ
engine.onLocaleChange((from, to) => {
console.log(`Locale changed: ${from} -> ${to}`);
});I18nEngineConfig æ¥å£
interface I18nEngineConfig {
defaultLocale?: string; // é»è®€è¯èš: 'en'
fallbackLocale?: string; // åéè¯èš: 'en'
debug?: boolean; // è°è¯æš¡åŒ: false
cache?: CacheConfig; // çŒåé
眮
plugins?: I18nPlugin[]; // åå§æä»¶å衚
onError?: (error: Error) => void; // é误å€ç
missingKeyHandler?: (key: string) => string; // 猺倱é®å€ç
}2. Cache System (@yyc3/i18n-core/cache)
髿§èœ LRU çŒåïŒæŸèæåç¿»è¯æ§èœã
import { LRUCache } from '@yyc3/i18n-core/cache';
// å建çŒåå®äŸ
const cache = new LRUCache<string, string>({
maxSize: 1000, // æå€§çŒåæ¡ç®
ttl: 3600000, // è¿ææ¶éŽ (1å°æ¶)
});
// ååæ°æ®
cache.set('key', 'value');
const value = cache.get('key'); // "value"
// çŒåæªåœäž
cache.get('nonexistent'); // undefined
// è·åç»è®¡ä¿¡æ¯
const stats: CacheStats = cache.getStats();
console.log(stats.hits, stats.misses, stats.size);
// æåšæž
ç
cache.clear();
cache.delete('key');æ§èœææ
| æäœ | æ¶éŽå€æåºŠ | å¹³åèæ¶ | |------|-----------|----------| | get() | O(1) | <0.1ms | | set() | O(1) | <0.1ms | | æ¹éæ¥æŸ | O(n) | <1ms (100 keys) |
3. Plugin System (@yyc3/i18n-core/plugins)
坿©å±çæä»¶æ¶æïŒæ¯æçåœåšæé©åã
import {
PluginManager,
createConsoleLogger,
MissingKeyReporter,
PerformanceTracker,
} from '@yyc3/i18n-core/plugins';
// 泚åå
眮æä»¶
i18n.plugins.register(createConsoleLogger());
i18n.plugins.register(new MissingKeyReporter().createPlugin());
i18n.plugins.register(new PerformanceTracker({ slowThreshold: 10 }).createPlugin());
// èªå®ä¹æä»¶
i18n.plugins.register({
name: 'analytics-plugin',
beforeTranslate(key: string) {
console.log(`[Analytics] Translating: ${key}`);
// è¿å true ç»§ç»æ§è¡ïŒè¿å false äžæ
return true;
},
afterTranslate(result: string, key: string) {
console.log(`[Analytics] Result: ${result}`);
},
onLocaleChange(from: string, to: string) {
console.log(`[Analytics] Locale: ${from} -> ${to}`);
},
});
// ç§»é€æä»¶
i18n.plugins.unregister('analytics-plugin');å 眮æä»¶å衚
| æä»¶å | çšé | é
眮项 |
|--------|------|--------|
| ConsoleLogger | åŒåè°è¯æ¥å¿ | æ |
| MissingKeyReporter | ç产ç¯å¢çŒºå€±é®çæ§ | reportUrl, threshold |
| PerformanceTracker | æ§èœææ æ¶é | slowThreshold, percentiles |
4. ICU MessageFormat (@yyc3/i18n-core/icu)
宿Žç ICU MessageFormat å®ç°ïŒæ¯æå€æ°ãéæ©çå€æè¯æ³ã
import { ICUParser, ICUCompiler } from '@yyc3/i18n-core/icu';
// è§£æ ICU è¯æ³
const parser = new ICUParser();
const ast = parser.parse('{count, plural, one {# item} other {# items}}');
// çŒè¯å¹¶æž²æ
const compiler = new ICUCompiler({ locale: 'en' });
const result = compiler.compile(ast, { count: 5 });
console.log(result); // "5 items"
// åšåŒæäžçŽæ¥äœ¿çš (èªå𿣿µ ICU è¯æ³)
t('items_count', { count: 5 }); // èªåšè·¯ç±å° ICU çŒè¯å𿝿ç ICU è¯æ³
| è¯æ³ç±»å | ç€ºäŸ | 诎æ |
|----------|------|------|
| Plural | {count, plural, one {...} other {...}} | 倿°è§å |
| Select | {gender, select, male {...} female {...}} | æ¡ä»¶éæ© |
| SelectOrdinal | {rank, selectOrdinal, one {...} two {...}} | åºæ°è¯ |
| Number | {price, number, ::currency/USD} | æ°åæ ŒåŒå |
| Date | {date, date, full} | æ¥ææ ŒåŒå |
| Time | {time, time, short} | æ¶éŽæ ŒåŒå |
| Offset | {offset, plural, offset:1 ...} | åç§»é |
5. AI Translation (@yyc3/i18n-core/ai)
LLM 驱åšçæºèœç¿»è¯ïŒæ¯æ OpenAI åæ¬å° Ollamaã
import {
AIProviderManager,
OpenAIProvider,
OllamaProvider,
QualityEstimator,
} from '@yyc3/i18n-core/ai';
// å建 AI 管çåš
const ai = new AIProviderManager();
// 泚å OpenAI Provider
ai.register(new OpenAIProvider({
apiKey: process.env.OPENAI_API_KEY!,
model: 'gpt-4o-mini',
}));
// ææ³šå Ollama Provider (å
莹æ¬å°æš¡å)
ai.register(new OllamaProvider({
baseUrl: 'http://localhost:11434',
model: 'qwen2.5:3b',
}));
// æ§è¡ç¿»è¯
const result = await ai.translate({
sourceText: 'Hello World',
sourceLocale: 'en',
targetLocale: 'zh-CN',
context: 'UI label for welcome message',
});
console.log(result.translatedText); // "äœ å¥œäžç"
console.log(result.confidence); // 0.95
// 莚éè¯äŒ°
const qe = new QualityEstimator();
const quality = await qe.estimate({
sourceText: 'Hello World',
translatedText: 'äœ å¥œäžç',
sourceLocale: 'en',
targetLocale: 'zh-CN',
});
console.log(quality.score); // 0.98
console.log(quality.issues); // [] (æ é®é¢)æ¯æç AI Provider
| Provider | ç±»å | ææ¬ | å»¶è¿ | éçšåºæ¯ | |----------|------|------|------|----------| | OpenAI | äºç«¯ API | ð°ð° | äœ (<2s) | ç产ç¯å¢ | | Ollama | æ¬å°æš¡å | å 莹 | äž (~3s) | åŒå/犻线 |
6. MCP Server (@yyc3/i18n-core/mcp)
Model Context Protocol æå¡ç«¯ïŒè®© AI Agent çŽæ¥äœ¿çš i18n å·¥å ·ã
import {
MCPServer,
registerI18nTools,
StdioTransport,
} from '@yyc3/i18n-core/mcp';
// å建 MCP Server
const server = new MCPServer({
name: 'yyc3-i18n-tools',
version: '1.4.0',
transport: new StdioTransport(),
});
// 泚å 7 䞪 i18n å·¥å
·
registerI18nTools(server);
// å¯åšæå¡
await server.start();
console.log('MCP Server running...');
// å¯çšå·¥å
·:
// 1. search_translations - æçŽ¢ç¿»è¯
// 2. add_translation_key - æ·»å ç¿»è¯é®
// 3. translate_key - ç¿»è¯å䞪é®
// 4. check_missing_keys - æ£æ¥çŒºå€±é®
// 5. get_locale_stats - è·åè¯èšç»è®¡
// 6. set_locale - 讟眮åœåè¯èš
// 7. quality_report - ç¿»è¯èŽšéæ¥åMCP å·¥å ·è¯Šæ
| å·¥å
·å | åèœ | åæ° |
|--------|------|------|
| search_translations | æçŽ¢ç¿»è¯å
容 | query, locale, limit |
| add_translation_key | æ·»å æ°ç¿»è¯é® | key, translations |
| translate_key | ç¿»è¯æå®é® | key, targetLocale |
| check_missing_keys | æ£æ¥çŒºå€±çç¿»è¯é® | locale |
| get_locale_stats | è·åè¯èšç»è®¡ä¿¡æ¯ | - |
| set_locale | 讟眮åœåè¯èš | locale |
| quality_report | çæèŽšéæ¥å | locale |
7. RTL Support (@yyc3/i18n-core â å
眮)
RTL (Right-to-Left) è¯èšçåçæ¯æã
import {
isRTL,
setupDocumentDirection,
flipSpacing,
getAlignment,
createMirroredLayout,
} from '@yyc3/i18n-core';
// æ£æµæ¯åŠäžº RTL è¯èš
isRTL('ar'); // true
isRTL('he'); // true
isRTL('en'); // false
// èªåšè®Ÿçœ®ææ¡£æ¹å
setupDocumentDirection('ar');
// <html dir="rtl" lang="ar">
// CSS 屿§ç¿»èœ¬
flipSpacing({ marginLeft: 10, marginRight: 20 }, 'rtl');
// â { marginLeft: 20, marginRight: 10 }
// è·åå¯¹éœæ¹åŒ
getAlignment('ltr'); // 'left'
getAlignment('rtl'); // 'right'
// å建éååžå±
const layout = createMirroredLayout('rtl');
layout.mainAxis; // 'right'
layout.crossAxis; // 'left'æ¯æç RTL è¯èš
| è¯èš | 代ç | æ¹å | |------|------|------| | é¿æäŒ¯è¯ | ar | RTL | | åžäŒ¯æ¥è¯ | he | RTL | | æ³¢æ¯è¯ | fa | RTL | | ä¹å°éœè¯ | ur | RTL |
ð³ åè·¯åŸå¯Œå ¥ïŒTree ShakingïŒ
æéåŒå ¥ïŒæèŽäŒåæå äœç§¯
// å
šéšåèœ (~15KB gzipped)
import { i18n, t, I18nEngine } from '@yyc3/i18n-core'
// ä»
çŒåç³»ç» (~3KB gzipped)
import { LRUCache } from '@yyc3/i18n-core/cache'
// ä»
æä»¶ç³»ç» (~4KB gzipped)
import { PluginManager, createConsoleLogger } from '@yyc3/i18n-core/plugins'
// ä»
ICU åŒæ (~5KB gzipped)
import { ICUParser, ICUCompiler } from '@yyc3/i18n-core/icu'
// ä»
AI ç¿»è¯ (~4KB gzipped)
import { AIProviderManager, OpenAIProvider } from '@yyc3/i18n-core/ai'
// ä»
MCP æå¡ç«¯ (~3KB gzipped)
import { MCPServer, registerI18nTools } from '@yyc3/i18n-core/mcp'å¯çšåè·¯åŸ
| åè·¯åŸ | 倧å°äŒ°è®¡(gzipped) | 富åºå
容 | éçšåºæ¯ |
|--------|-------------------|----------|----------|
| . | ~15KB | å
šéšåèœ | å°åé¡¹ç® / å¿«éåå |
| ./cache | ~3KB | LRU Cache ç³»ç» | éèŠé«æ§èœçŒå |
| ./plugins | ~4KB | æä»¶ç®¡çåš + å
眮æä»¶ | éèŠæ©å±åèœ |
| ./icu | ~5KB | ICU è§£æåš/çŒè¯åš | å€ææ¶æ¯æ ŒåŒ |
| ./ai | ~4KB | AI ç¿»è¯ + 莚éè¯äŒ° | éèŠ LLM ç¿»è¯ |
| ./mcp | ~3KB | MCP Server + Transport | AI Agent éæ |
æå äœç§¯å¯¹æ¯
åºæ¯ | æªäŒå | Tree Shakingå | èç
-------------------------|--------|----------------|------
宿Žå¯Œå
¥ | 15KB | 15KB | -
ä»
çš Core + Cache | 15KB | 8KB | 47%
ä»
çš ICU åŒæ | 15KB | 5KB | 67%
ä»
çš AI Translation | 15KB | 4KB | 73%
ä»
çš MCP Server | 15KB | 3KB | 80%ð API åè
æ žå¿å¯Œåº
// ====== äž»å
¥å£ ======
import {
// Engine
I18nEngine, i18n, t,
// Cache
LRUCache,
type CacheConfig, type CacheStats,
// Plugins
PluginManager,
createConsoleLogger, MissingKeyReporter, PerformanceTracker,
type I18nContext, type I18nPlugin,
// Formatter
formatRelativeTime, interpolate, pluralize,
type TranslateParams,
// Locale Detection
detectSystemLocale, isChineseLocale, normalizeLocale,
type LocaleDetectionResult,
// RTL Utilities
isRTL, setupDocumentDirection, flipSpacing, getAlignment,
getDirection, getOppositeAlignment, mirrorPosition,
createMirroredLayout, transformClassForRTL,
type HorizontalAlignment, type TextDirection,
// Types
type Locale, type RTLLocale, type TranslationMap,
} from '@yyc3/i18n-core'
// ====== åè·¯åŸå¯Œå
¥ ======
// Cache
import { LRUCache } from '@yyc3/i18n-core/cache'
// Plugins
import { PluginManager, createConsoleLogger } from '@yyc3/i18n-core/plugins'
// ICU
import { ICUParser, ICUCompiler } from '@yyc3/i18n-core/icu'
// AI
import { AIProviderManager, OpenAIProvider, QualityEstimator } from '@yyc3/i18n-core/ai'
// MCP
import { MCPServer, registerI18nTools, StdioTransport } from '@yyc3/i18n-core/mcp'æ žå¿ API
t() â ç¿»è¯åœæ°
function t(key: string, params?: TranslateParams): string;
// åºæ¬çšæ³
t('welcome'); // "Welcome"
// åæ°æåŒ
t('greeting', { name: 'World' }); // "Hello, World!"
// åµå¥å¯¹è±¡
t('user.profile.name'); // ä»åµå¥å¯¹è±¡è·å
// ICU è¯æ³ (èªå𿣿µ)
t('items', { count: 5 }); // "5 items"i18n â å
šå±åäŸ
// å
šå±åäŸïŒæ éå建å®äŸ
const i18n: I18nEngine;
// 忢è¯èš
await i18n.setLocale('zh-CN');
// è·ååœåè¯èš
i18n.currentLocale; // 'zh-CN'
// æ¹éç¿»è¯
const results = await i18n.batchTranslate(['k1', 'k2', 'k3']);
// è·åç»è®¡
const stats = i18n.getStats();
// äºä»¶çå¬
i18n.onLocaleChange((from, to) => {});I18nEngine â åŒæç±»
class I18nEngine {
constructor(config?: I18nEngineConfig);
init(): Promise<void>;
destroy(): Promise<void>;
setLocale(locale: string): Promise<void>;
t(key: string, params?: TranslateParams): string;
batchTranslate(keys: string[]): Promise<Record<string, string>>;
getTranslations(): TranslationMap;
getStats(): EngineStats;
onLocaleChange(callback: (from: string, to: string) => void): () => void;
plugins: PluginManager;
}ð§ é«çº§åèœ
1. åœåç©ºéŽ (Namespaces)
import { createNamespace } from '@yyc3/i18n-core';
// å建åœå空éŽ
const userNS = createNamespace('user');
const productNS = createNamespace('product');
// 䜿çšåœå空éŽ
userNS.t('profile.name'); // user.profile.name
productNS.t('detail.title'); // product.detail.title2. è°è¯æš¡åŒ
const engine = new I18nEngine({ debug: true });
// æµè§åšæ§å¶å°è®¿é®
window.__i18n_debug__ = {
engine,
getCurrentState: () => ({
locale: engine.currentLocale,
cache: engine.cache.getStats(),
plugins: engine.plugins.list(),
}),
};
// åŒåæ¶æ¥çç¶æ
console.log(window.__i18n_debug__.getCurrentState());3. å€å®äŸæ¯æ
import { I18nEngine } from '@yyc3/i18n-core';
// å建å€äžªç¬ç«å®äŸ
const adminI18n = new I18nEngine({ defaultLocale: 'zh-CN' });
const userI18n = new I18nEngine({ defaultLocale: 'en' });
// åèªç¬ç«è¿è¡
adminI18n.t('dashboard.title'); // äžæ
userI18n.t('home.welcome'); // English4. èªå®ä¹é误å€ç
const engine = new I18nEngine({
onError: (error) => {
// åéå°é误远螪æå¡
Sentry.captureException(error);
},
missingKeyHandler: (key) => {
console.warn(`[i18n] Missing key: ${key}`);
return `[MISSING: ${key}]`; // è¿åå äœç¬Š
},
});5. æ§èœçæ§
import { PerformanceTracker } from '@yyc3/i18n-core/plugins';
const tracker = new PerformanceTracker({
slowThreshold: 10, // æ
¢æ¥è¯¢éåŒ (ms)
percentiles: [50, 90, 95, 99],
}).createPlugin();
i18n.plugins.register(tracker);
// è·åæ§èœæ¥å
const report = tracker.getReport();
console.log(report.percentiles.p95); // 95åäœå»¶è¿
console.log(report.slowQueries); // æ
¢æ¥è¯¢åè¡šðš æäœ³å®è·µ
â æšèåæ³
䜿çšå šå±åäŸè¿è¡ç®å项ç®
import { i18n, t } from '@yyc3/i18n-core'; t('key'); // ç®æŽæäºäœ¿çšç¬ç«å®äŸè¿è¡å€§å项ç®
const engine = new I18nEngine({ /* config */ });å©çš Tree Shaking åå°äœç§¯
import { ICUParser } from '@yyc3/i18n-core/icu'; // ä» åŒå ¥æéæš¡åå¯çšç产ç¯å¢æä»¶
if (process.env.NODE_ENV === 'production') { i18n.plugins.register(new MissingKeyReporter().createPlugin()); }ç»å AI ç¿»è¯æåæç
const ai = new AIProviderManager(); ai.register(new OllamaProvider()); // å 莹æ¬å°ç¿»è¯
â é¿å åæ³
äžèŠåšåŸªç¯äžé¢ç¹è°çš
setLocale// â é误: æ§èœé®é¢ for (const item of items) { await i18n.setLocale(item.locale); } // â æ£ç¡®: æ¹éå€ç const translations = await i18n.batchTranslate(keys);äžèŠå¿œç¥é误å€ç
// â é误: å¯èœæåŒåžž t(userProvidedKey); // â æ£ç¡®: é 眮 missingKeyHandler const engine = new I18nEngine({ missingKeyHandler: (key) => `[${key}]`, });äžèŠåšç产ç¯å¢åŒå¯ debug æš¡åŒ
// â é误: æ³é²å éšä¿¡æ¯ const engine = new I18nEngine({ debug: true }); // â æ£ç¡®: ä» åŒåç¯å¢ const engine = new I18nEngine({ debug: process.env.NODE_ENV === 'development', });
𧪠æµè¯èŠç
æµè¯ç©éµ
| æš¡å | æµè¯æä»¶ | çšäŸæ° | éè¿ç | èŠçç | |------|----------|--------|--------|--------| | Core Engine | engine-v2.test.ts | 45 | 100% | 94% | | Cache | (å«åš engine tests) | 30 | 100% | 96% | | Plugins | plugins.test.ts | 25 | 100% | 91% | | Formatter | formatter.test.ts | 35 | 100% | 93% | | Detector | detector.test.ts | 20 | 100% | 89% | | RTL Utils | rtl-utils.test.ts | 28 | 100% | 92% | | Translate | translate.test.ts, translate-full.test.ts | 60 | 100% | 95% | | ICU Parser/Compiler | icu/parser-compiler.test.ts | 40 | 100% | 90% | | AI Providers | ai/providers.test.ts | 35 | 100% | 87% | | Quality Estimator | ai/quality-estimator.test.ts | 25 | 100% | 85% | | MCP Server | mcp/server.test.ts, mcp/i18n-tools.test.ts | 40 | 100% | 88% | | Security | security/.test.ts | 30 | 100% | 93% | | Infrastructure | infra/.test.ts | 30 | 100% | 90% | | æ»è®¡ | 28 files | 443 | â 100% | 92.5% |
è¿è¡æµè¯
# å
šéæµè¯
pnpm test
# ç嬿š¡åŒ (åŒåæ¶)
pnpm test:watch
# èŠççæ¥å
pnpm test:coverage
# å䞪æä»¶
pnpm test -- engine-v2.test.ts
# å¹é
æµè¯å
pnpm test -- -t "should translate with interpolation"â¡ æ§èœåºå
ååºæ¶éŽ (Node.js 20, M2 Mac)
| æäœ | å¹³åèæ¶ | P99 èæ¶ | QPS | |------|----------|----------|-----| | ç®åç¿»è¯ (çŒååœäž) | 0.02ms | 0.05ms | 50,000+ | | åžŠåæ°ç¿»è¯ | 0.05ms | 0.12ms | 20,000+ | | ICU 倿°è§å | 0.15ms | 0.35ms | 6,500+ | | æ¹éç¿»è¯ (100 keys) | 8ms | 15ms | 12,000+ | | AI ç¿»è¯ (Ollama) | 1500ms | 3000ms | 0.67 | | AI ç¿»è¯ (OpenAI) | 800ms | 2000ms | 1.25 |
å åå çš
| åºæ¯ | å åå çš | 诎æ | |------|----------|------| | ç©ºåŒæ | ~2MB | åºç¡åŒé | | å 蜜 1 è¯èš | ~5MB | å«ç¿»è¯æ°æ® | | å 蜜 10 è¯èš | ~25MB | å šéšå 眮è¯èš | | çŒå 10000 æ¡ç® | ~8MB | LRU Cache | | AI Provider | ~50MB | æš¡åå 蜜 |
ð¡ïž å®å šç¹æ§
OWASP Level 4 å®å šæ å
| å®å šç»ŽåºŠ | å®ç°æ¹åŒ | ç¶æ | |----------|----------|------| | ReDoS 鲿€ | å®å šæ£åçŒè¯ïŒè¶ æ¶äžæ | â | | æ¶åºæ»å»é²æ€ | åžžéæ¶éŽå笊䞲æ¯èŸ | â | | è·¯åŸéå鲿€ | ç®åœééžæ£æµäžé»æ¢ | â | | æ³šå ¥æ£æµ | SQL/Command æ³šå ¥æš¡åŒè¯å« | â | | å å¯éæºæ° | äœ¿çš crypto.randomBytes | â |
å®å šç€ºäŸ
import { safeRegexCompile } from '@yyc3/i18n-core/lib/security/safe-regex';
import { constantTimeStringEqual } from '@yyc3/i18n-core/lib/security/secret-equal';
import { guardAgainstPathTraversal } from '@yyc3/i18n-core/lib/utils/path-guards';
// å®å
šçæ£å衚蟟åŒçŒè¯ (é² ReDoS)
const regex = safeRegexCompile(userInput, { timeout: 1000 });
// åžžéæ¶éŽæ¯èŸ (鲿¶åºæ»å»)
const isEqual = constantTimeStringEqual(tokenA, tokenB);
// è·¯åŸéå鲿€
guardAgainstPathTraversal('../../etc/passwd'); // æåº SecurityErrorâ åžžè§é®é¢
Q1: åŠäœæ·»å èªå®ä¹è¯èšïŒ
import { i18n } from '@yyc3/i18n-core';
// åšæå 蜜翻è¯
await i18n.addLocale('custom-LANG', {
welcome: '欢è¿',
greeting: 'äœ å¥œïŒ{name}ïŒ',
});Q2: åŠäœåš SSR (æå¡ç«¯æž²æ) äžäœ¿çšïŒ
// Next.js 瀺äŸ
import { I18nEngine } from '@yyc3/i18n-core';
let engine: I18nEngine;
export async function getServerSideProps(context) {
engine = new I18nEngine();
await engine.init();
await engine.setLocale(context.locale);
return {
props: {
initialLocale: context.locale,
translations: engine.getTranslations(),
},
};
}Q3: åŠäœå€çåšæç¿»è¯é®ïŒ
// äœ¿çš missingKeyHandler æŠæªæªç¥é®
const engine = new I18nEngine({
missingKeyHandler: (key) => {
// åéå°ç¿»è¯æå¡æè¿åé»è®€åŒ
logMissingKeyToServer(key);
return humanizeKey(key); // 'user.profile.name' â 'User Profile Name'
},
});Q4: AI ç¿»è¯ç莚éåŠäœä¿è¯ïŒ
import { QualityEstimator } from '@yyc3/i18n-core/ai';
const qe = new QualityEstimator();
const result = await qe.estimate({
sourceText: 'Hello',
translatedText: 'äœ å¥œ',
sourceLocale: 'en',
targetLocale: 'zh-CN',
});
if (result.score > 0.9 && result.issues.length === 0) {
console.log('â
ç¿»è¯èŽšéäŒç§');
} else {
console.log('â ïž éèŠäººå·¥å®¡æ ž:', result.issues);
}Q5: åŠäœäžç°æ i18n æ¹æ¡è¿ç§»ïŒ
请åè诊ç»ç è¿ç§»æåïŒæ¶µçä» react-i18nextãvue-i18n çæ¹æ¡ç宿Žè¿ç§»æ¥éª€ã
ð è¿ç§»æå
ä» v1.x åçº§å° v2.x
äž»èŠ Breaking Changes:
- å
ååæŽ:
@yyc3/i18nâ@yyc3/i18n-core - ç±»åéåœå:
I18nManagerâI18nEngine - æä»¶æ¥å£æŽæ°: æ°å¢å¿
éç
name屿§ - é çœ®æ ŒåŒè°æŽ: Cache å Debug é çœ®ç»æåå
诊ç»è¿ç§»æ¥éª€è¯·åé MIGRATION_GUIDE.mdã
ð€ èŽ¡ç®æå
æä»¬æ¬¢è¿ç€ŸåºèŽ¡ç®ïŒè¯·éµåŸªä»¥äžæµçšïŒ
- Fork æ¬ä»åº
- åå»ºç¹æ§åæ¯ (
git checkout -b feature/amazing-feature) - æäº€æŽæ¹ (
git commit -m 'Add amazing feature') - æšé忝 (
git push origin feature/amazing-feature) - åŒå¯ Pull Request
åŒåæµçš
# å
éä»åº
git clone https://github.com/YanYuCloudCube/Family-PAI.git
# å®è£
äŸèµ
pnpm install
# è¿å
¥ i18n-core å
ç®åœ
cd packages/i18n-core
# åŒåæš¡åŒ
pnpm build:watch
# è¿è¡æµè¯
pnpm test
# ç±»åæ£æ¥
pnpm lint
# æ ŒåŒå代ç
pnpm format代ç è§è
- TypeScript strict mode
- äžºå ¬å ± API æ·»å JSDoc 泚é
- æ°åèœå¿ é¡»å å«å¯¹åºæµè¯
- éµåŸª OWASP å®å šçŒç è§è
ð License
MIT © YYC³ AI Team
YYC³ AI Family â å «äœæäººåAIå®¶äººçæºèœäžæ¢
äºé« · äºæ · äºå · äºç»Ž
ææ¡£çæ¬: 2.3.0 | æåæŽæ°: 2026-04-24
