feat: npm package

This commit is contained in:
xxnuo
2026-01-01 17:08:25 +08:00
parent 159862ba67
commit 1ca6edad20
10 changed files with 224 additions and 12 deletions

52
example/README.md Normal file
View File

@@ -0,0 +1,52 @@
# MTranServer Usage Examples
This directory contains examples of how to use `mtranserver` programmatically in your Node.js/Bun applications.
## Prerequisites
Ensure you have installed the library:
```bash
bun install mtranserver@latest
```
## Running the Example
You can run the example using `bun`:
```bash
bun example/usage.ts
```
## Code Explanation
The key component is the `MTran` class:
```typescript
import { MTran } from 'mtranserver';
// 1. Create instance
const mtran = new MTran({
modelDir: './models' // Directory to store translation models
});
// 2. Initialize
await mtran.init();
// 3. Translate
const result = await mtran.translate('en', 'es', 'Hello world');
console.log(result); // "Hola mundo"
// 4. Cleanup
await mtran.close();
```
## Configuration
The `MTran` constructor accepts a config object with the following optional properties:
- `modelDir`: Path to store downloaded models.
- `configDir`: Path to store configuration files (like `records.json`).
- `logLevel`: Logging verbosity ('debug', 'info', 'warn', 'error').
- `workersPerLanguage`: Number of parallel workers for translation.
- `enableOfflineMode`: If true, disables auto-downloading of models.

52
example/usage.ts Normal file
View File

@@ -0,0 +1,52 @@
import { MTran } from 'mtranserver';
async function main() {
console.log('--- MTranServer Library Usage Example ---');
// 1. Initialize MTran instance
// You can pass configuration options here.
const mtran = new MTran();
try {
// 2. Initialize (loads records, prepares environment)
console.log('Initializing MTran...');
await mtran.init();
const fromLang = 'en';
const toLang = 'zh';
const text = 'Hello world! This is a library test.';
// 3. (Optional) Ensure the model is downloaded.
// .translate() will error if the model is missing in offline mode,
// or attempt to download it if online (default).
// Calling downloadModel explicitly is good for pre-warming.
console.log(`Checking model for ${fromLang} -> ${toLang}...`);
await mtran.downloadModel(fromLang, toLang);
// 4. Perform Translation
console.log(`
Translating: "${text}"`);
const start = performance.now();
const result = await mtran.translate(fromLang, toLang, text);
const end = performance.now();
console.log(`Translation Result: "${result}"`);
console.log(`Time taken: ${(end - start).toFixed(2)}ms`);
// 5. Language Detection
console.log('\nDetecting language for: "Bonjour tout le monde"');
const detected = await mtran.detect("Bonjour tout le monde");
console.log(`Detected: ${detected}`);
} catch (error) {
console.error('An error occurred:', error);
} finally {
// 6. Cleanup
// Important: Stops background worker threads to allow process to exit cleanly
console.log('\nCleaning up...');
await mtran.close();
}
}
main();

View File

@@ -3,11 +3,13 @@
"version": "4.0.13",
"type": "module",
"description": "Translation server",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"bin": {
"mtranserver": "./dist/main.js"
},
"files": [
"mtranserver",
"dist",
"assets"
],
"scripts": {
@@ -17,6 +19,8 @@
"build:docker": "bun scripts/build.ts --docker",
"build:node": "bun run gen && rm -rf dist && bun build src/main.ts --outdir dist --target node --format esm --sourcemap --external zstd-wasm-decoder --external express",
"build:dev": "bun run gen && rm -rf dist && bun build src/main.ts --outdir dist --target node --format esm --sourcemap --external zstd-wasm-decoder --external express",
"build:lib": "bun run gen && rm -rf dist && rm -f ui/dist/assets/*.d.ts && bun build src/index.ts src/main.ts --outdir dist --target node --format esm --sourcemap --external zstd-wasm-decoder --external express && tsc -p tsconfig.lib.json",
"prepublishOnly": "bun run build:lib",
"start": "node dist/main.js",
"test": "bun test",
"typecheck": "tsc --noEmit",

View File

@@ -134,3 +134,8 @@ export function getConfig(): Config {
return globalConfig;
}
export function setConfig(config: Partial<Config>) {
const current = getConfig();
globalConfig = { ...current, ...config };
}

20
src/globals.d.ts vendored
View File

@@ -2,3 +2,23 @@ declare module '*.wasm' {
const path: string;
export default path;
}
declare module '*.css' {
const path: string;
export default path;
}
declare module '*.js' {
const path: string;
export default path;
}
declare module '*.html' {
const path: string;
export default path;
}
declare module '*.png' {
const path: string;
export default path;
}

View File

@@ -1,8 +0,0 @@
export * from '@/config/index.js';
export * from '@/logger/index.js';
export * from '@/utils/index.js';
export * from '@/version/index.js';
export * from '@/models/index.js';
export * from '@/services/index.js';
export * from '@/middleware/index.js';
export * from '@/server/index.js';

68
src/mtran.ts Normal file
View File

@@ -0,0 +1,68 @@
import { setConfig, getConfig, Config } from '@/config/index.js';
import { initRecords, downloadModel } from '@/models/index.js';
import { translateWithPivot, cleanupAllEngines } from '@/services/index.js';
import { detectLanguage } from '@/services/detector.js';
import { normalizeLanguageCode } from '@/utils/index.js';
export interface MTranConfig extends Partial<Config> {}
export class MTran {
constructor(config?: MTranConfig) {
if (config) {
setConfig(config);
}
}
/**
* Initialize the translation engine (load records, etc.)
*/
async init(): Promise<void> {
await initRecords();
}
/**
* Translate text
* @param from Source language code (or 'auto')
* @param to Target language code
* @param text Text to translate
* @param html Whether the text is HTML (default: false)
*/
async translate(from: string, to: string, text: string, html: boolean = false): Promise<string> {
const normalizedFrom = from === 'auto' ? 'auto' : normalizeLanguageCode(from);
const normalizedTo = normalizeLanguageCode(to);
return translateWithPivot(normalizedFrom, normalizedTo, text, html);
}
/**
* Detect language of the text
* @param text Text to analyze
*/
async detect(text: string): Promise<string | null> {
return detectLanguage(text);
}
/**
* Ensure model is downloaded for a language pair
* @param from Source language
* @param to Target language
*/
async downloadModel(from: string, to: string): Promise<void> {
const normalizedFrom = normalizeLanguageCode(from);
const normalizedTo = normalizeLanguageCode(to);
await downloadModel(normalizedTo, normalizedFrom);
}
/**
* Clean up all loaded engines and release memory
*/
async close(): Promise<void> {
cleanupAllEngines();
}
/**
* Get current configuration
*/
getConfig(): Config {
return getConfig();
}
}

View File

@@ -1,3 +1,5 @@
import path from 'path';
import { fileURLToPath } from 'url';
import loadCLD2 from '@/lib/cld2/cld2.js';
import wasmPath from '@/lib/cld2/cld2.wasm' with { type: 'file' };
import * as logger from '@/logger/index.js';
@@ -28,7 +30,10 @@ async function initCLD(): Promise<void> {
try {
logger.debug('Initializing CLD2 language detector');
const wasmBuffer = await readFile(wasmPath);
const currentDir = path.dirname(fileURLToPath(import.meta.url));
const absoluteWasmPath = path.resolve(currentDir, wasmPath);
const wasmBuffer = await readFile(absoluteWasmPath);
const module: any = await loadCLD2({
print: (msg: string) => logger.debug(`[CLD2]: ${msg}`),

View File

@@ -1,4 +1,5 @@
import path from 'path';
import { fileURLToPath } from 'url';
import { readFile } from 'fs/promises';
import { TranslationEngine } from '@/core/engine.js';
import { createResourceLoader } from '@/core/factory.js';
@@ -69,8 +70,11 @@ async function getOrCreateSingleEngine(
const engine = new TranslationEngine();
const loader = createResourceLoader();
logger.debug(`Loading WASM from: ${wasmPath}`);
const wasmBinary = await readFile(wasmPath);
const currentDir = path.dirname(fileURLToPath(import.meta.url));
const absoluteWasmPath = path.resolve(currentDir, wasmPath);
logger.debug(`Loading WASM from: ${absoluteWasmPath}`);
const wasmBinary = await readFile(absoluteWasmPath);
logger.debug(`WASM loaded, size: ${wasmBinary.byteLength} bytes`);
const bergamotModule = await loader.loadBergamotModule(wasmBinary, loadBergamot);

10
tsconfig.lib.json Normal file
View File

@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"emitDeclarationOnly": true,
"noEmit": false,
"types": ["node"]
},
"include": [],
"files": ["src/index.ts", "src/globals.d.ts"]
}