diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5f568f1 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,43 @@ +{ + // Use IntelliSense to learn about possible Node.js debug attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "JavaScript Test using SourceMap", + "program": "${workspaceRoot}/test/index.ts", + "cwd": "${workspaceRoot}", + "args": [ + //---- + // Unable to reset DB in debugging mode. + //---- + // Therefore, reset DB first by running + // `npm run reset-for-debugging` command, + // and run debugging mode later. + //---- + "--reset", "false", + + //---- + // You can run specific test functions + //---- + "--include", "compiler_", + "--exclude", "index", + ], + "outFiles": ["${workspaceRoot}/bin/**/*.js"], + }, + { + "type": "node", + "request": "launch", + "name": "Compiler Manual Testing", + "cwd": "${workspaceRoot}", + "args": [ + "${workspaceRoot}/test/manual/compiler/result.ts" + ], + "runtimeArgs": ["-r", "ts-node/register"], + "outFiles": ["${workspaceRoot}/bin/**/*.js"], + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0b116e7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "editor.tabSize": 2, + "editor.formatOnSave": true, + "[javascript][typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + }, + "[prisma]": { + "editor.defaultFormatter": "Prisma.prisma", + } +} diff --git a/.vscode/workflows/build.yml b/.vscode/workflows/build.yml new file mode 100644 index 0000000..1e8dfd8 --- /dev/null +++ b/.vscode/workflows/build.yml @@ -0,0 +1,28 @@ +name: build +on: + push: + paths: + - 'assets/**' + - 'src/**' + - 'test/**' + - 'package.json' + pull_request: + paths: + - 'assets/input/**' + - 'src/**' + - 'test/**' + - 'package.json' +jobs: + Ubuntu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20.x + - name: Install dependencies + run: npm install + - name: Build + run: npm run build + - name: Test + run: npm run test \ No newline at end of file diff --git a/.vscode/workflows/release.yml b/.vscode/workflows/release.yml new file mode 100644 index 0000000..37df384 --- /dev/null +++ b/.vscode/workflows/release.yml @@ -0,0 +1,24 @@ +name: release +on: + release: + types: [created] +jobs: + publish: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: none + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20.x + registry-url: https://registry.npmjs.org/ + - name: Install dependencies + run: npm install + - name: Build + run: npm run build + - name: Publish to npm + run: npm publish --provenance --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH }} \ No newline at end of file diff --git a/README.md b/README.md index 5dc3ae2..a467569 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Translate JSON file via Google Translate API. -`@samchon/json-translate` is a wrapper library that translates JSON files using the Google Translate API. It has optimization logic that reduces costs and elapsed time by minimizing the number of API calls. +`@samchon/json-translate` is a wrapper library that translates JSON files using the Google Translate API. It provides a more convenient way to translate JSON data, including optimization strategies recuding the cost and elapsed time of the translation by minimizing the number of the API calls. Here is an example code translating `swagger.json` file. @@ -27,8 +27,9 @@ const main = async (): Promise => { }); const input: OpenApi.IDocument = await fetchOpenApiDocument(); const output: OpenApi.IDocument = await translator.translate({ - input, - to: "ko", + input, // JSON input data to translate + target: "ko", // target language to translate + // source: "en", // current language, but not essential filter: (explore) => explore.key === "title" || explore.key === "description" || @@ -38,4 +39,11 @@ const main = async (): Promise => { console.log(output); }; main().catch(console.error); -``` \ No newline at end of file +``` + +Also, you can see some example cases: + +English (source) | Korean | Japanese | Arabic +--------|--------|----------|-------- +[bbs.article.json](https://github.com/samchon/json-translator/blob/master/assets/input/bbs.article.json) | [bbs.ko.json](https://github.com/samchon/json-translator/blob/master/assets/output/bbs.article.ko.json) | [bbs.ja.json](https://github.com/samchon/json-translator/blob/master/assets/output/bbs.article.ja.json) | [bbs.ar.json](https://github.com/samchon/json-translator/blob/master/assets/output/bbs.article.ar.json) +[shopping.swagger.json](https://github.com/samchon/json-translator/blob/master/assets/input/shopping.swagger.json) | [shopping.ko.json](https://github.com/samchon/json-translator/blob/master/assets/output/shopping.swagger.ko.json) | [shopping.ja.json](https://github.com/samchon/json-translator/blob/master/assets/output/shopping.swagger.ja.json) | [shopping.ar.json](https://github.com/samchon/json-translator/blob/master/assets/output/shopping.swagger.ar.json) \ No newline at end of file diff --git a/package.json b/package.json index 64aa4d7..23da00d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@samchon/json-translator", - "version": "0.1.0", + "version": "0.1.1", "description": "", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/JsonTranslator.ts b/src/JsonTranslator.ts index b715b37..1818ef9 100644 --- a/src/JsonTranslator.ts +++ b/src/JsonTranslator.ts @@ -1,5 +1,18 @@ import { Translate } from "@google-cloud/translate/build/src/v2"; +/** + * JSON Translator. + * + * `JsonTranslator` is a class translating JSON data into another language. + * + * It wraps the `@google-cloud/translate` module and provides + * a more convenient way to translate JSON data, including optimization + * strategy reducing the cost and elapsed time of the translation by + * minimizing the number of the API calls. + * + * @reference + * @author Jeongho Nam - https://github.com/samchon + */ export class JsonTranslator { private readonly service_: Translate; @@ -7,11 +20,17 @@ export class JsonTranslator { this.service_ = new Translate(options); } + /** + * Translate JSON data. + * + * @param props Properties for the translation. + * @returns The translated JSON data. + */ public async translate(props: JsonTranslator.IProps): Promise { const collection: ICollection = prepareCollection(props); const translated: string[] = []; const from: string | undefined = - props.from ?? (await this.detect(collection.raw)); + props.source ?? (await this.detect(collection.raw)); let queue: Array = []; let bytes: number = 0; @@ -21,7 +40,7 @@ export class JsonTranslator { queue.map((p) => p.text), { from, - to: props.to, + to: props.target, }, ); translated.push(...response); @@ -57,6 +76,12 @@ export class JsonTranslator { return collection.output; } + /** + * Detect the language of the texts. + * + * @param texts Texts to detect the language. + * @returns The detected language or `undefined` if the language is unknown. + */ public async detect(texts: string[]): Promise { if (texts.length === 0) return undefined; const [response] = await this.service_.detect( @@ -70,17 +95,61 @@ export class JsonTranslator { } } export namespace JsonTranslator { + /** + * Properties for the translation. + */ export interface IProps { + /** + * The JSON input data to translate. + */ input: T; - from?: string; - to: string; + + /** + * Source language code. + */ + source?: string; + + /** + * Target language code. + */ + target: string; + + /** + * Filter function specifying which data to translate. + * + * @param explore Information about the data to explore. + * @returns `true` if the data should be translated; otherwise, `false`. + */ filter?: (explore: IExplore) => boolean; } + + /** + * Exploration information used in the {@link IProps.filter} function. + */ export interface IExplore { + /** + * The parent object instance. + */ object: object | null; + + /** + * The property key containing the {@link value} + */ key: string | null; + + /** + * Index number if the {@link value} is an array element. + */ index: number | null; + + /** + * Accessor path to the {@link value}. + */ accessor: string[]; + + /** + * The string value to translate. + */ value: string; } } @@ -158,6 +227,7 @@ const visitCollectionData = (next: { ...next.explore, object: next.value, key, + index: null, accessor: [...next.explore.accessor, key], }, setter: (x) => (next.value[key] = x), diff --git a/test/features/test_bbs_article.ts b/test/features/test_bbs_article.ts index 31892fd..da292ca 100644 --- a/test/features/test_bbs_article.ts +++ b/test/features/test_bbs_article.ts @@ -12,7 +12,7 @@ export const test_bbs_article = async ( JSON.stringify( await translator.translate({ input, - to: lang, + target: lang, filter: ({ key }) => key === "title" || key === "body", }), null, diff --git a/test/features/test_connector_swagger.ts b/test/features/test_connector_swagger.ts deleted file mode 100644 index fd72b62..0000000 --- a/test/features/test_connector_swagger.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { JsonTranslator } from "@samchon/translate-json"; -import fs from "fs"; - -import input from "../../assets/input/connector.swagger.json"; - -export const test_connector_swagger = async ( - translator: JsonTranslator, -): Promise => { - for (const lang of ["ko", "ja", "ar"]) - await fs.promises.writeFile( - `${__dirname}/../../../assets/output/connector.swagger.${lang}.json`, - JSON.stringify( - await translator.translate({ - input, - to: lang, - filter: (explore) => - explore.key === "title" || - explore.key === "description" || - explore.key === "summary" || - explore.key === "x-wrtn-placeholder", - }), - null, - 2, - ), - "utf8", - ); -}; diff --git a/test/features/test_shopping_swagger.ts b/test/features/test_shopping_swagger.ts index fee6465..790c0a6 100644 --- a/test/features/test_shopping_swagger.ts +++ b/test/features/test_shopping_swagger.ts @@ -12,7 +12,7 @@ export const test_connector_swagger = async ( JSON.stringify( await translator.translate({ input, - to: lang, + target: lang, filter: (explore) => explore.key === "title" || explore.key === "description" ||