From a6e0b060bcaf8fc1a5052f571e75759ce358a7a9 Mon Sep 17 00:00:00 2001 From: MiracleUFO Date: Wed, 11 Oct 2023 20:25:35 +0100 Subject: [PATCH] perf(proxying): fix Buffer not found issue by using proxy This fixes the broken behavior of the component library. BREAKING CHANGE Moved translation request to proxy servers. Issues with translation package necessitated moving some request logic to a proxy --- .env.development | 2 +- README.md | 13 +++++--- package-lock.json | 54 +------------------------------- package.json | 1 - rollup.config.mjs | 1 + src/constants/index.ts | 10 ++++-- src/utils/getTranslation.ts | 22 ++++--------- src/utils/translate.ts | 62 +++++++++++++++++++++++++++++++++++++ 8 files changed, 86 insertions(+), 79 deletions(-) create mode 100644 src/utils/translate.ts diff --git a/.env.development b/.env.development index 9307495..adafb44 100644 --- a/.env.development +++ b/.env.development @@ -1,3 +1,3 @@ NODE_ENV=development -TRANSLATE_API_PROXY= +TEST_TRANSLATE_API_PROXY= diff --git a/README.md b/README.md index 456f132..fa0abe9 100644 --- a/README.md +++ b/README.md @@ -13,17 +13,20 @@ A modern, *free*, *lightweight* npm package for translating react apps (pages an - Allows for custom language list files. (Coming in v2.0.0) ## Install -```npm install react-g-translator``` +```npm install @miracleufo/react-g-translator``` or with yarn -```yarn add react-g-translator``` +```yarn add @miracleufo/react-g-translator``` ## Usage +> DISCLAIMER! +To be 100% legal please use the official [Google Translate API](https://cloud.google.com/translate). This project is mainly for pet projects and prototyping 😉. Also, only use the most recent version of this package. + ### To translate whole component: ```jsx -import Translator from 'react-g-translator'; +import Translator from '@miracleufo/react-g-translator'; return ( @@ -34,7 +37,7 @@ return ( ### To translate specific text inline: ```jsx -import { Translate } from 'react-g-translator'; +import { Translate } from '@miracleufo/react-g-translator'; return (
@@ -48,7 +51,7 @@ return ( ### To get translation of text directly: ```jsx -import { getTranslation } from 'react-g-translator'; +import { getTranslation } from '@miracleufo/react-g-translator'; const helloInIgbo = await getTranslation('Hello', 'en', 'ig'); diff --git a/package-lock.json b/package-lock.json index 0bb7aa6..f10d172 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,12 +13,11 @@ "@testing-library/jest-dom": "^6.1.3", "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.5.0", - "@vitalets/google-translate-api": "^9.0.0", "crypto-js": "^4.1.1", "env-cmd": "^10.1.0", "https-proxy-agent": "^7.0.2", "lodash": "^4.17.21", - "react": "^18.2.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^18.2.0", "react-query": "^3.39.2", "react-scripts": "5.0.1", @@ -5997,19 +5996,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@vitalets/google-translate-api": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@vitalets/google-translate-api/-/google-translate-api-9.2.0.tgz", - "integrity": "sha512-w98IPWGuexlGmh8Y19AxF6cgWT0U5JLevVNDKEuFpTWtBC9z3YtDWKTDxF3nPP1k9bWicuB1V7I7YfHoZiDScw==", - "dependencies": { - "@types/http-errors": "^1.8.2", - "http-errors": "^2.0.0", - "node-fetch": "^2.6.7" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/@webassemblyjs/ast": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", @@ -18595,44 +18581,6 @@ "lodash": "^4.17.21" } }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", diff --git a/package.json b/package.json index d9bdf84..37240e6 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,6 @@ "@testing-library/jest-dom": "^6.1.3", "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.5.0", - "@vitalets/google-translate-api": "^9.0.0", "crypto-js": "^4.1.1", "env-cmd": "^10.1.0", "https-proxy-agent": "^7.0.2", diff --git a/rollup.config.mjs b/rollup.config.mjs index 63e95dd..ef6e103 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -41,6 +41,7 @@ const mainConfig = { ], external: ['react', 'react-dom'], plugins: [ + // @ts-ignore peerDepsExternal(), nodeResolve(), commonjs(), diff --git a/src/constants/index.ts b/src/constants/index.ts index c8579b3..e4554e7 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,13 +1,15 @@ import language from '../types/language'; -const { NODE_ENV, TRANSLATE_API_PROXY } = process.env; +const { NODE_ENV, TEST_TRANSLATE_API_PROXY } = process.env; // NODE ENVIRONMENT const NODE_DEVELOPMENT = 'development'; const NODE_TEST = 'test'; const IS_DEVELOPMENT_OR_TEST = NODE_ENV && [NODE_DEVELOPMENT, NODE_TEST].includes(NODE_ENV); -const PROXY = TRANSLATE_API_PROXY; +const PROXY_URL = 'https://react-g-translator-proxy.vercel.app/api'; +const PROXY_URL_ALT = 'https://react-g-translator-proxy-express.onrender.com/translate'; +const PROXY_URL_TEST = TEST_TRANSLATE_API_PROXY; const DEFAULT_PROPS = { from: 'en', @@ -41,7 +43,9 @@ export { NODE_DEVELOPMENT, NODE_TEST, IS_DEVELOPMENT_OR_TEST, - PROXY, + PROXY_URL, + PROXY_URL_ALT, + PROXY_URL_TEST, DEFAULT_PROPS, DEFAULT_QUERY_OPTIONS, DEFAULT_LANGUAGE_FROM, diff --git a/src/utils/getTranslation.ts b/src/utils/getTranslation.ts index 1ca4fbd..0b8c9f2 100644 --- a/src/utils/getTranslation.ts +++ b/src/utils/getTranslation.ts @@ -1,14 +1,8 @@ -import { translate } from '@vitalets/google-translate-api'; -import { HttpsProxyAgent } from 'https-proxy-agent'; import throttle from 'lodash/throttle'; - import chunkRequest from './chunkRequest'; -import { - PROXY, - CHARACTER_LIMIT, - DEBOUNCE_RATE, - IS_DEVELOPMENT_OR_TEST, -} from '../constants'; +import translate from './translate'; + +import { CHARACTER_LIMIT, DEBOUNCE_RATE } from '../constants'; import language from '../types/language'; const getTranslation = async ( @@ -16,14 +10,10 @@ const getTranslation = async ( from?: language, to?: language, ) : Promise => { - // opts for development / testing - // in case `TooManyRequestsError` or Error Code `429` - const fetchOptions = IS_DEVELOPMENT_OR_TEST && PROXY && { agent: new HttpsProxyAgent(PROXY) }; - - // translating happens here. ✨ bing! ✨ const translateRequest = async (chunk: string | string[]) => { - const translation = await translate(chunk as string, { from, to, fetchOptions }); - return translation.text; + // translating happens here. ✨ bing! ✨ + const translation = await translate(chunk as string, from, to); + return JSON.parse(translation)?.text ?? ''; }; return throttle( diff --git a/src/utils/translate.ts b/src/utils/translate.ts new file mode 100644 index 0000000..22a64a1 --- /dev/null +++ b/src/utils/translate.ts @@ -0,0 +1,62 @@ +import { HttpsProxyAgent } from 'https-proxy-agent'; +import getErrorInTranslationMessage from './getErrorInTranslationMessage'; +import { + IS_DEVELOPMENT_OR_TEST, + PROXY_URL, + PROXY_URL_ALT, + PROXY_URL_TEST, +} from '../constants'; +import language from '../types/language'; + +const translate = async (text: string, from?: language, to?: language) => { + // option for development / testing + // in case of `TooManyRequestsError` (Error Code `429`) + const fetchOptions = ( + IS_DEVELOPMENT_OR_TEST && PROXY_URL_TEST && { agent: new HttpsProxyAgent(PROXY_URL_TEST) } + ); + + let response; + try { + response = await fetch(PROXY_URL, { + credentials: 'omit', + mode: 'cors', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify( + { + text, + from, + to, + fetchOptions, + }, + ), + }); + } catch (error) { + // If the first request failed, try with the second proxy + response = await fetch(PROXY_URL_ALT, { + credentials: 'omit', + mode: 'cors', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify( + { + text, + from, + to, + fetchOptions, + }, + ), + }); + } + + if (response.status === 200) return response.json(); + + const error = new Error(`${response.status} - ${response.statusText}`); + throw getErrorInTranslationMessage(error); +}; + +export default translate;