Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feat: lobe-i18n - 添加 Markdown 文件翻译缓存功能 (#98) #99

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/lobe-i18n/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"type-check": "tsc --noEmit"
},
"dependencies": {
"@deno/kv": "^0.7.0",
"@inkjs/ui": "^1",
"@langchain/core": "latest",
"@langchain/openai": "latest",
Expand Down
38 changes: 38 additions & 0 deletions packages/lobe-i18n/src/cache/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Kv, openKv } from '@deno/kv';
import crypto from 'node:crypto';

export class FileHashCache {
kv: Promise<Kv>;
constructor(cacheDB = '.lobe-i18n.db') {
this.kv = openKv(cacheDB);
}
getHash(fileContent: string) {
return crypto.createHash('md5').update(fileContent).digest('hex');
}
setHash(filePath: string, key: string) {
return this.kv.then((kv) => {
return kv.set(['files', filePath], key);
});
}
hasHash(filePath: string, key: string) {
return this.kv
.then((kv) => {
return kv.get<string>(['files', filePath]);
})
.then((res) => {
return res.value === key;
});
}
remove(filePath: string) {
return this.kv.then((kv) => kv.delete(['files', filePath]));
}
clear() {
return this.kv.then(async (kv) => {
const iter = kv.list<string>({ prefix: ['files'] });
for await (const res of iter) kv.delete(res.key);
});
}
destory() {
return this.kv.then((kv) => kv.close());
}
}
8 changes: 7 additions & 1 deletion packages/lobe-i18n/src/cli.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ notifier.notify({ isGlobal: true });
const program = new Command();

interface Flags {
clear?: boolean;
config?: string;
option?: boolean;
withMd?: boolean;
Expand All @@ -27,6 +28,7 @@ program
.version(packageJson.version)
.addOption(new Option('-o, --option', 'Setup lobe-i18n preferences'))
.addOption(new Option('-c, --config <string>', 'Specify the configuration file'))
.addOption(new Option('--clear', 'clear cache'))
.addOption(
new Option('-m, --with-md', 'Run i18n translation and markdown translation simultaneously'),
);
Expand All @@ -45,7 +47,11 @@ program.command('locale', { isDefault: true }).action(async () => {
program.command('md').action(async () => {
const options: Flags = program.opts();
if (options.config) explorer.loadCustomConfig(options.config);
await new TranslateMarkdown().start();
const service = new TranslateMarkdown();
if (options.clear) {
await service.fileHashCache.clear();
}
await service.start();
});

program.parse();
22 changes: 15 additions & 7 deletions packages/lobe-i18n/src/commands/TranslateMarkdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { consola } from 'consola';
import { globSync } from 'glob';
import matter from 'gray-matter';
import { isString } from 'lodash-es';
import { existsSync } from 'node:fs';
import { relative, resolve } from 'node:path';

import { FileHashCache } from '@/cache';
import Progress from '@/components/Progress';
import { I18n, I18nMarkdownWriteOptions } from '@/core/I18n';
import { selectors } from '@/store';
Expand All @@ -21,6 +21,7 @@ class TranslateMarkdown {
markdownConfig: MarkdownConfig;
query: I18nMarkdownWriteOptions[] = [];
i18n: I18n;
fileHashCache = new FileHashCache();
constructor() {
this.markdownConfig = selectors.getMarkdownConfigFile();
const defaultConfig = selectors.getConfigFile();
Expand Down Expand Up @@ -55,7 +56,7 @@ class TranslateMarkdown {

if (!files || files.length === 0) alert.error('No markdown entry was found.', true);

this.genFilesQuery(files);
await this.genFilesQuery(files);

if (this.query.length > 0) {
await this.runQuery();
Expand All @@ -64,7 +65,6 @@ class TranslateMarkdown {
}
consola.success('All i18n tasks have been completed!');
}

async runQuery() {
consola.info(
`Current model setting: ${chalk.cyan(this.config.modelName)} (temperature: ${chalk.cyan(
Expand Down Expand Up @@ -101,6 +101,7 @@ class TranslateMarkdown {
}

writeMarkdown(item.filename, mdResut);
await this.fileHashCache.setHash(item.filePath, item.hash!);
totalTokenUsage += data.tokenUsage;
consola.success(chalk.yellow(outputPath), chalk.gray(`[Token usage: ${data.tokenUsage}]`));
} else {
Expand All @@ -110,7 +111,7 @@ class TranslateMarkdown {
if (totalTokenUsage > 0) consola.info('Total token usage:', chalk.cyan(totalTokenUsage));
}

genFilesQuery(files: string[], skipLog?: boolean) {
async genFilesQuery(files: string[], skipLog?: boolean) {
const config = this.markdownConfig;
if (!skipLog)
consola.start(
Expand All @@ -121,24 +122,31 @@ class TranslateMarkdown {
for (const file of files) {
try {
const md = readMarkdown(file);
const hash = this.fileHashCache.getHash(md);
const cacheKey = await this.fileHashCache.hasHash(file, hash);
if (cacheKey) {
consola.info(`✅ Cahced: ${chalk.green(file)}`);
continue;
}
for (const locale of config.outputLocales || []) {
const targetExtension = this.getTargetExtension(locale, file, md);
const targetFilename = this.getTargetFilename(file, targetExtension);
if (existsSync(targetFilename)) continue;
const mode = this.getMode(file, md);
const { data, content } = matter(md);
consola.info(`📄 To ${locale}: ${chalk.yellow(targetFilename)}`);
this.query.push({
filePath: file,
filename: targetFilename,
from: config.entryLocale,
hash,
matter: data,
md: this.markdownConfig.includeMatter ? md : content,
mode,
to: locale,
});
}
} catch {
alert.error(`${file} not found`, true);
} catch (error: unknown) {
alert.error(`${file} not found. ` + (error as Error).message, true);
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/lobe-i18n/src/core/I18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export interface I18nTranslateOptions {

export interface I18nMarkdownTranslateOptions
extends Pick<I18nTranslateOptions, 'from' | 'to' | 'onProgress'> {
filePath: string;
hash: string;
matter?: any;
md: string;
mode: MarkdownModeType;
Expand Down
Loading