From 427a60eb9ec78c3f0f832872bd0a6fa3b9df6bf8 Mon Sep 17 00:00:00 2001 From: gabriel-logan Date: Sun, 2 Jun 2024 18:08:24 -0300 Subject: [PATCH] feat: Updating documentation and adapting code to support any dynamic JSON structure --- .gitignore | 2 +- .npmignore | 1 + README.md | 49 ++++++-- package.json | 2 +- src/index.ts | 9 +- src/translate/index.ts | 8 +- src/translateToMultipleFolders/index.ts | 9 +- src/translateToUnicFolder/index.ts | 8 +- src/types/index.ts | 6 + src/updateTranslationMulti/index.ts | 103 +++++++++------- src/updateTranslationUnic/index.ts | 152 ++++++++++++++++++++++++ 11 files changed, 275 insertions(+), 74 deletions(-) create mode 100644 src/types/index.ts create mode 100644 src/updateTranslationUnic/index.ts diff --git a/.gitignore b/.gitignore index 80f0eef..5564170 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ node_modules/ private/ dist/ -types/ +/types/ tsconfigcopy.json geraTraducoes.js diff --git a/.npmignore b/.npmignore index 1f7f380..ebe451c 100644 --- a/.npmignore +++ b/.npmignore @@ -18,3 +18,4 @@ tsconfigcopy.json tsconfig.json tsconfig.types.json .prettierrc.js +dist/types/ diff --git a/README.md b/README.md index ff941f7..47cebcc 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ NPM PAGE: https://www.npmjs.com/package/azure-translator-code GITHUB PAGE: https://github.com/gabriel-logan/Azure-translator-code +You con test the library in the following link: https://azuretranslatorcode.vercel.app + ## Installation To get started, you can install the library via npm: @@ -49,14 +51,15 @@ const jsonFile = require('./jsonFileToTranslate/en.json'); // or -// IMPORTANT -// The file must follow this structure. const jsonFile = { - "translation": { - "welcome": "Welcome", - "hello": "Hello", - } -}; + "HomePage": { + "welcome": 'Welcome', + "hello": 'Hello', + "SubText": { + "subText": 'This is a subtext' + } + } +} ``` Now, you can use the library to translate the JSON file into multiple languages: @@ -148,6 +151,38 @@ updateTranslationsMulti(key, endpoint, location, fromLang, toLangs, jsonFile, 'm // This function will update the translations in the folder called myFolder ``` +## Translate and Log the result + +You can also log the result of the translation in the console. + +```javascript +const { translate } = require('azure-translator-code'); + +const key = 'sds12312a213aaaa9b2d0c37eds37b'; // REPLACE WITH YOUR OWN KEY HERE +const endpoint = 'https://api.cognitive.microsofttranslator.com/'; +const location = 'eastus'; +const fromLang = 'en'; +const toLang = 'pt'; +const jsonFile = { + HomePage: { + Welcome: "Welcome", + Hello: "Hello", + }, +}; + +translate(key, endpoint, location, fromLang, toLang, jsonFile).then((result) => console.log(result)); + +// Output +/** + * { + * "translation": { + * "Welcome": "Bem-vindo", + * "Hello": "Olá", + * } + * } + */ + +``` Make sure to replace the key and endpoint information with your own Azure access credentials. Ensure that the JSON file and settings are correctly defined according to your needs. Contributing diff --git a/package.json b/package.json index 2f2a436..e086ac0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azure-translator-code", - "version": "1.1.2", + "version": "1.1.3", "description": "Azure Cognitive Services Translator Text API Code for Use with Common Languages", "author": { "name": "Gabriel Logan" diff --git a/src/index.ts b/src/index.ts index 1612917..85eb69f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,13 @@ import translateToMultipleFolders from './translateToMultipleFolders'; import translateToUnicFolder from './translateToUnicFolder'; import updateTranslationsMulti from './updateTranslationMulti'; +import updateTranslationsUnic from './updateTranslationUnic'; import translate from './translate'; -export { translateToMultipleFolders, translateToUnicFolder, updateTranslationsMulti, translate }; +export { + translateToMultipleFolders, + translateToUnicFolder, + updateTranslationsMulti, + updateTranslationsUnic, + translate, +}; diff --git a/src/translate/index.ts b/src/translate/index.ts index 2aa50ce..b4adb35 100644 --- a/src/translate/index.ts +++ b/src/translate/index.ts @@ -1,12 +1,6 @@ import axios from 'axios'; import { v4 as uuidv4 } from 'uuid'; - -/** - * Represents the type of a translation object. - */ -export type TranslationType = { - [key: string]: string | TranslationType; -}; +import type { TranslationType } from '../types'; async function translateText( text: string, diff --git a/src/translateToMultipleFolders/index.ts b/src/translateToMultipleFolders/index.ts index 5ec89e8..efa3e99 100644 --- a/src/translateToMultipleFolders/index.ts +++ b/src/translateToMultipleFolders/index.ts @@ -2,10 +2,7 @@ import axios from 'axios'; import { v4 as uuidv4 } from 'uuid'; import * as fs from 'fs'; import * as path from 'path'; - -export type TranslationType = { - [key: string]: string | TranslationType; -}; +import type { TranslationType } from '../types'; /** * @param key Your key from azure translator, something like: 'sds12312a213aaaa9b2d0c37eds37b' @@ -28,7 +25,7 @@ export type TranslationType = { 'tlh-Latn' ]; * @param jsonFile - * It must follow the following structure: + * It must be a valid JSON object: * * { "translation": { @@ -44,8 +41,6 @@ export type TranslationType = { "error_message": "An error occurred" } } - * - If you need, copy this structure to get better then make your modification * @param [folderNamePath='multiFolderGeneratedTranslations'] If it is undefined, it will be associated by default: multiFolderGeneratedTranslations You can use this like: 'myfoldername' or 'myfoldername/otherfolder' or './myfoldername/etcfolder' diff --git a/src/translateToUnicFolder/index.ts b/src/translateToUnicFolder/index.ts index bbe8258..0225533 100644 --- a/src/translateToUnicFolder/index.ts +++ b/src/translateToUnicFolder/index.ts @@ -2,10 +2,8 @@ import axios from 'axios'; import { v4 as uuidv4 } from 'uuid'; import * as fs from 'fs'; import * as path from 'path'; +import type { TranslationType } from '../types'; -export type TranslationType = { - [key: string]: string | TranslationType; -}; /** * @param key Your key from azure translator, something like: 'sds12312a213aaaa9b2d0c37eds37b' * @param endpoint The endpoint: 'https://api.cognitive.microsofttranslator.com/' @@ -27,7 +25,7 @@ export type TranslationType = { 'tlh-Latn' ]; * @param jsonFile - * It must follow the following structure: + * It must be a valid JSON object: * * { "translation": { @@ -43,8 +41,6 @@ export type TranslationType = { "error_message": "An error occurred" } } - * - If you need, copy this structure to get better then make your modification * @param [folderNamePath='unicFolderGeneratedTranslations'] If it is undefined, it will be associated by default: unicFolderGeneratedTranslations You can use this like: 'myfoldername' or 'myfoldername/otherfolder' or './myfoldername/etcfolder' diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..c3401db --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,6 @@ +/** + * Represents the type of a translation object. + */ +export type TranslationType = { + [key: string]: string | TranslationType; +}; diff --git a/src/updateTranslationMulti/index.ts b/src/updateTranslationMulti/index.ts index cc9dc6c..6f2ac98 100644 --- a/src/updateTranslationMulti/index.ts +++ b/src/updateTranslationMulti/index.ts @@ -1,11 +1,8 @@ import axios from 'axios'; import { v4 as uuidv4 } from 'uuid'; -import fs from 'fs'; -import path from 'path'; - -export interface TranslationType { - translation: Record; -} +import * as fs from 'fs'; +import * as path from 'path'; +import type { TranslationType } from '../types'; /** * @param key Your key from azure translator, something like: 'sds12312a213aaaa9b2d0c37eds37b' @@ -28,7 +25,7 @@ export interface TranslationType { 'tlh-Latn' ]; * @param jsonFile - * It must follow the following structure: + * It must be a valid JSON object: * * { "translation": { @@ -44,8 +41,6 @@ export interface TranslationType { "error_message": "An error occurred" } } - * - If you need, copy this structure to get better then make your modification * * @description This function checks the json with the already existing translations and adds only the non-existing translations to the file, this serves to save data. * Otherwise it works the same as the other 2 functions @@ -62,16 +57,15 @@ export default function updateTranslationsMulti( fromLang: string, toLangs: string[], jsonFile: TranslationType, - folderNamePath: string = 'multiFolderGeneratedTranslations', + folderNamePath: string = 'multiFolderGeneratedTranslations', // Onde sera salvo os arquivos ): void { - const rootDir: string = path.join(__dirname, '..', '..', '..', '..'); - const traducoesDir: string = path.join(rootDir, folderNamePath); + const traducoesDir: string = path.join(__dirname, '..', '..', '..', '..', folderNamePath); if (!fs.existsSync(traducoesDir)) { fs.mkdirSync(traducoesDir, { recursive: true }); } - async function translateText(text: string, from: string, to: string) { + function translateText(text: string, from: string, to: string) { return axios({ baseURL: endpoint, url: '/translate', @@ -96,48 +90,69 @@ export default function updateTranslationsMulti( }); } - async function translateAndSave(lang: string) { - const langDir: string = path.join(traducoesDir, lang); - - if (!fs.existsSync(langDir)) { - fs.mkdirSync(langDir); - } - - const outputFileName: string = path.join(langDir, `${lang}.json`); - let translations: Record = {}; - - if (fs.existsSync(outputFileName)) { - const existingData: TranslationType = JSON.parse(fs.readFileSync(outputFileName, 'utf8')); - translations = existingData.translation; - } + async function translateAndSave( + lang: string, + obj: TranslationType, + existingTranslations: TranslationType, + currentPath: string = '', + ) { + const translations: Record = existingTranslations; - const newTranslations: Record = {}; + for (const key in obj) { + const newPath = currentPath ? `${currentPath}.${key}` : key; - for (const key in jsonFile.translation) { - if (!translations[key]) { - try { - console.log('TO FAZENDO UMA REQUISICAO'); - const response = await translateText(jsonFile.translation[key], fromLang, lang); - const translatedText: string = response.data[0].translations[0].text; - newTranslations[key] = translatedText; - console.log(`Translating "${jsonFile.translation[key]}" to ${lang} \n\n`); - } catch (error) { - if (error instanceof Error) { - console.error(`Error translating "${key}" to ${lang}: ${error.message} \n`); - } else { - console.error(`An error occurred within the error (: \n`); + if (typeof obj[key] === 'object' && obj[key] !== null) { + const nestedTranslations = await translateAndSave( + lang, + obj[key] as TranslationType, + (existingTranslations[key] || {}) as TranslationType, + newPath, + ); + translations[key] = nestedTranslations; + } else { + if (!translations[key]) { + try { + const response = await translateText(obj[key] as string, fromLang, lang); + const translatedText = response.data[0].translations[0].text; + translations[key] = translatedText; + console.log(`Translating ${obj[key]} to ${lang} \n\n`); + } catch (error) { + if (error instanceof Error) { + console.error(`Error translating "${newPath}" to ${lang}: ${error.message} \n`); + } else { + console.error(`An error occurred within the error (: \n`); + } } } } } - translations = { ...translations, ...newTranslations }; - fs.writeFileSync(outputFileName, JSON.stringify({ translation: translations }, null, 4)); + const langDir = path.join(traducoesDir, lang); + + if (!fs.existsSync(langDir)) { + fs.mkdirSync(langDir); + } + + const outputFileName = path.join(langDir, `${lang}.json`); + fs.writeFileSync(outputFileName, JSON.stringify(translations, null, 4)); console.log(`Translations for ${lang} saved in ${outputFileName} \n\n`); + + return translations; } async function translateAndSaveAll() { - const translationPromises: Promise[] = toLangs.map((lang) => translateAndSave(lang)); + const translationPromises = toLangs.map(async (lang) => { + const langDir = path.join(traducoesDir, lang); + const outputFileName = path.join(langDir, `${lang}.json`); + + let existingTranslations: TranslationType = {}; + if (fs.existsSync(outputFileName)) { + const rawData = fs.readFileSync(outputFileName, 'utf8'); + existingTranslations = JSON.parse(rawData); + } + + return translateAndSave(lang, jsonFile, existingTranslations); + }); await Promise.all(translationPromises); } diff --git a/src/updateTranslationUnic/index.ts b/src/updateTranslationUnic/index.ts new file mode 100644 index 0000000..6c4d03d --- /dev/null +++ b/src/updateTranslationUnic/index.ts @@ -0,0 +1,152 @@ +import axios from 'axios'; +import { v4 as uuidv4 } from 'uuid'; +import * as fs from 'fs'; +import * as path from 'path'; +import type { TranslationType } from '../types'; + +/** + * @param key Your key from azure translator, something like: 'sds12312a213aaaa9b2d0c37eds37b' + * @param endpoint The endpoint: 'https://api.cognitive.microsofttranslator.com/' + * @param location Ex. 'eastus' + * @param fromLang Ex. 'en' + * @param toLangs Ex. [ + 'pt', + 'de', + 'es', + 'fr', + 'it', + 'ja', + 'ko', + 'nl', + 'ru', + 'zh', + 'pt-pt', + 'ar', + 'tlh-Latn' + ]; + * @param jsonFile + * It must be a valid JSON object: + * + * { + "translation": { + "welcome": "Welcome", + "hello": "Hello", + "good_morning": "Good morning", + "good_afternoon": "Good afternoon", + "good_evening": "Good evening", + "thank_you": "Thank you", + "please": "Please", + "yes": "Yes", + "no": "No", + "error_message": "An error occurred" + } + } + * + * @description This function checks the json with the already existing translations and adds only the non-existing translations to the file, this serves to save data. + * Otherwise it works the same as the other 2 functions + * + * @param [folderNamePath='unicFolderGeneratedTranslations'] If it is undefined, it will be associated by default: unicFolderGeneratedTranslations + You can use this like: 'myfoldername' or 'myfoldername/otherfolder' or './myfoldername/etcfolder' + @IMPORTANT Saving always starts from the project root folder. + @return {void} This function will return a folder called folder unicFolderGeneratedTranslations in root folder or YourChoice + */ +export default function updateTranslationsUnic( + key: string, + endpoint: string, + location: string, + fromLang: string, + toLangs: string[], + jsonFile: TranslationType, + folderNamePath: string = 'unicFolderGeneratedTranslations', // Onde sera salvo os arquivos +): void { + const traducoesDir: string = path.join(__dirname, '..', '..', '..', '..', folderNamePath); + + if (!fs.existsSync(traducoesDir)) { + fs.mkdirSync(traducoesDir, { recursive: true }); + } + + function translateText(text: string, from: string, to: string) { + return axios({ + baseURL: endpoint, + url: '/translate', + method: 'post', + headers: { + 'Ocp-Apim-Subscription-Key': key, + 'Ocp-Apim-Subscription-Region': location, + 'Content-type': 'application/json', + 'X-ClientTraceId': uuidv4().toString(), + }, + params: { + 'api-version': '3.0', + from: from, + to: to, + }, + data: [ + { + text: text, + }, + ], + responseType: 'json', + }); + } + + async function translateAndSave( + lang: string, + obj: TranslationType, + existingTranslations: TranslationType, + ) { + const translations: Record = existingTranslations; + + for (const key in obj) { + if (typeof obj[key] === 'object' && obj[key] !== null) { + const nestedTranslations = await translateAndSave( + lang, + obj[key] as TranslationType, + (existingTranslations[key] || {}) as TranslationType, + ); + translations[key] = nestedTranslations; + } else { + if (!translations[key]) { + try { + const response = await translateText(obj[key] as string, fromLang, lang); + const translatedText = response.data[0].translations[0].text; + translations[key] = translatedText; + console.log(`Translating ${obj[key]} to ${lang} \n\n`); + } catch (error) { + if (error instanceof Error) { + console.error(`Error translating "${key}" to ${lang}: ${error.message} \n`); + } else { + console.error(`An error occurred within the error (: \n`); + } + } + } + } + } + + const outputFileName = path.join(traducoesDir, `${lang}.json`); + fs.writeFileSync(outputFileName, JSON.stringify(translations, null, 4)); + console.log(`Translations for ${lang} saved in ${outputFileName} \n\n`); + + return translations; + } + + async function translateAndSaveAll() { + const translationPromises = toLangs.map(async (lang) => { + const outputFileName = path.join(traducoesDir, `${lang}.json`); + + let existingTranslations: TranslationType = {}; + if (fs.existsSync(outputFileName)) { + const rawData = fs.readFileSync(outputFileName, 'utf8'); + existingTranslations = JSON.parse(rawData); + } + + return translateAndSave(lang, jsonFile, existingTranslations); + }); + + await Promise.all(translationPromises); + } + + translateAndSaveAll().catch((error) => { + console.error(`Error translating and saving texts: ${error.message} \n`); + }); +}