diff --git a/README.md b/README.md index 204d0b2..b729d1a 100644 --- a/README.md +++ b/README.md @@ -70,10 +70,10 @@ return ( - *Type string*. If string provided is not found in [supported languages](https://cloud.google.com/translate/docs/languages) will default to *user's current browser langauge setting*. - Overriden by [`setLanguageTo`](#hook-setlanguageto) hook. (Coming in V2) - - `shouldFallback`: Should translation return original text if error in translation (fallback) or return empty string. Optional. + - `shouldFallback`: Determines error handling. In displays original text when true, or empty string otherwise (when error). Optional. - Defaults to `true`. - *Type boolean*. If not provided will default to true. - - **NOTE:** Returns exception when there is an error in translation if set to `false`. + - **NOTE:** Will always log exception when there is an error in translation. ### Wrapper: `` ### *Type:* React functional component @@ -103,10 +103,16 @@ See [Usage](#to-get-translation-of-text-directly) *Type:* Function -*Returns*: string +*Returns*: string | Error *Params:* - `text`: *Type string*, *required* - [`from`](#props) *optional* - [`to`](#props) *optional* -- [`shouldFallback`](#props) *optional* \ No newline at end of file + + +## SPECIAL CASES + +- [`from`](#props) and [`to`](#props) being the same will return original text (determined by google translation API.) +- [`from`](#props) and [`to`](#props) being empty strings will be extrapolated from 'en' and *user's current browser langauge setting* respectively. +- `text` is not in `from` language and google translate API cannot detect language automatically will return the original text. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1a75d79..08439bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.5.0", "@vitalets/google-translate-api": "^9.0.0", + "https-proxy-agent": "^7.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-query": "^3.39.2", @@ -4287,19 +4288,6 @@ "node": ">= 14" } }, - "node_modules/@semantic-release/github/node_modules/https-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", - "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", - "dev": true, - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/@semantic-release/github/node_modules/slash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", @@ -12147,15 +12135,26 @@ } }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", "dependencies": { - "agent-base": "6", + "agent-base": "^7.0.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" } }, "node_modules/human-signals": { @@ -14340,6 +14339,18 @@ "node": ">= 6" } }, + "node_modules/jest-environment-jsdom/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/jest-environment-jsdom/node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -17040,6 +17051,19 @@ "jsdom": ">=10.0.0" } }, + "node_modules/jsdom/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", diff --git a/package.json b/package.json index 9d3208b..f91be08 100644 --- a/package.json +++ b/package.json @@ -97,6 +97,7 @@ "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.5.0", "@vitalets/google-translate-api": "^9.0.0", + "https-proxy-agent": "^7.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-query": "^3.39.2", diff --git a/src/components/Translate.tsx b/src/components/Translate.tsx index cb67718..93e7e6b 100644 --- a/src/components/Translate.tsx +++ b/src/components/Translate.tsx @@ -1,12 +1,9 @@ -import { useEffect } from 'react'; import { QueryClient, QueryClientProvider } from 'react-query'; import { LanguageProvider } from '../context/languageContext'; import useTranslation from '../queries/useTranslation'; -import getErrorInTranslationMessage from '../utils/getErrorInTranslationMessage'; import determineRenderedText from '../utils/determineRenderedText'; - import { DEFAULT_PROPS } from '../constants'; import language from '../types/language'; @@ -25,20 +22,10 @@ const Translate = ({ }) => { const { data, - error, isError, isLoading, } = useTranslation(children, from, to); - useEffect(() => { - // if shouldFallback prop is set to `false` and there's an error: - // throw error - if ( - isError - && (typeof shouldFallback !== 'undefined' && !shouldFallback) - ) throw getErrorInTranslationMessage(error); - }, [isError, error, shouldFallback]); - return ( diff --git a/src/constants/index.ts b/src/constants/index.ts index 83db585..ae3c661 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -10,18 +10,27 @@ const DEFAULT_PROPS = { shouldFallback: true, }; +const DEFAULT_QUERY_OPTIONS = { + defaultOptions: { + queries: { + retry: false, + }, + }, +}; + const DEFAULT_LANGUAGE_FROM: language = 'en'; const DEFAULT_BROWSER_LANGUAGE : language = window?.navigator?.language.startsWith('zh') ? window?.navigator?.language as language : window?.navigator?.language.split('-')[0] as language; -const TRANSLATION_NOT_FOUND_MESSAGE = 'Err 404: No translation found. Check `to` & `from` props.'; +const TRANSLATION_NOT_FOUND_MESSAGE = 'react-g-translator: Err 404: No translation found. Check `to` & `from` props.'; export { HELLO_IN_ENGLISH, HELLO_IN_FRENCH, HELLO_IN_SPANISH, DEFAULT_PROPS, + DEFAULT_QUERY_OPTIONS, DEFAULT_LANGUAGE_FROM, DEFAULT_BROWSER_LANGUAGE, TRANSLATION_NOT_FOUND_MESSAGE, diff --git a/src/queries/useTranslation.ts b/src/queries/useTranslation.ts index 723edcd..e7c29e8 100644 --- a/src/queries/useTranslation.ts +++ b/src/queries/useTranslation.ts @@ -1,6 +1,8 @@ import { useQuery } from 'react-query'; import { useLanguageContext } from '../context/languageContext'; + import getTranslation from '../utils/getTranslation'; +import getErrorInTranslationMessage from '../utils/getErrorInTranslationMessage'; import language from '../types/language'; const useTranslation = ( @@ -14,7 +16,16 @@ const useTranslation = ( error, isError, isLoading, - } = useQuery('translation', () => getTranslation(text, from || languageFrom, to || languageTo)); + } = useQuery( + 'translation', + () => getTranslation(text, from || languageFrom, to || languageTo), + { + // more descriptive error than react-query error + onError: (err: unknown) => { + console.error(getErrorInTranslationMessage(err)); + }, + }, + ); return { data, diff --git a/src/scripts/getTranslation.ts b/src/scripts/getTranslation.ts index c64dbc3..e706c61 100644 --- a/src/scripts/getTranslation.ts +++ b/src/scripts/getTranslation.ts @@ -12,24 +12,12 @@ const getTranslation = async ( text: string, from = DEFAULT_LANGUAGE_FROM, to = DEFAULT_BROWSER_LANGUAGE, - shouldFallback = true, ) : Promise => { - // loading state - let translation: string | undefined = text; - try { - translation = await getTranslationUtil(text, from, to); - - if ( - !translation - && (typeof shouldFallback !== 'undefined' && !shouldFallback) - ) throw new Error(TRANSLATION_NOT_FOUND_MESSAGE); + const translation = await getTranslationUtil(text, from, to); - return ( - translation - || (shouldFallback && text) - || '' - ); + if (translation) return translation; + throw new Error(TRANSLATION_NOT_FOUND_MESSAGE); } catch (error) { throw getErrorInTranslationMessage(error); } diff --git a/src/tests/utils-test.tsx b/src/tests/utils-test.tsx index ec1254b..938eed2 100644 --- a/src/tests/utils-test.tsx +++ b/src/tests/utils-test.tsx @@ -2,8 +2,10 @@ import { ReactElement, ReactNode } from 'react'; import { render, RenderOptions } from '@testing-library/react'; import { QueryClientProvider, QueryClient } from 'react-query'; import { LanguageProvider } from '../context/languageContext'; +import { DEFAULT_QUERY_OPTIONS } from '../constants'; -const queryClient = new QueryClient(); +const queryClient = new QueryClient(DEFAULT_QUERY_OPTIONS); +queryClient.clear(); const Providers = ({ children }: {children: ReactNode}) => ( @@ -18,14 +20,5 @@ const customRender = ( options?: Omit, ) => render(ui, { wrapper: Providers, ...options }); -const HELLO_IN_ENGLISH = 'Hello World'; -const HELLO_IN_SPANISH = 'Hola Mundo'; -const HELLO_IN_FRENCH = 'Bonjour Monde'; - export * from '@testing-library/react'; export { customRender as render }; -export { - HELLO_IN_ENGLISH, - HELLO_IN_SPANISH, - HELLO_IN_FRENCH, -}; diff --git a/src/utils/determineRenderedText.ts b/src/utils/determineRenderedText.ts index dee6f92..0ad7519 100644 --- a/src/utils/determineRenderedText.ts +++ b/src/utils/determineRenderedText.ts @@ -5,10 +5,10 @@ const determineRenderedText = ( isError: boolean, isLoading: boolean, ) => { + if (translatedText) return translatedText; if (shouldFallback && (isError || isLoading)) return text; if (isLoading) return text; if (isError) return ''; - if (translatedText) return translatedText; return ''; }; diff --git a/src/utils/getErrorInTranslationMessage.ts b/src/utils/getErrorInTranslationMessage.ts index ffd0529..5318131 100644 --- a/src/utils/getErrorInTranslationMessage.ts +++ b/src/utils/getErrorInTranslationMessage.ts @@ -1,3 +1,3 @@ -const getErrorInTranslationMessage = ((error: unknown) => new Error(`Error in translation: ${String(error)}`)); +const getErrorInTranslationMessage = ((error: unknown) => new Error(`react-g-translator: Error in translation: ${String(error)}`)); export default getErrorInTranslationMessage; diff --git a/src/utils/getTranslation.ts b/src/utils/getTranslation.ts index 8a8a7bc..21f55c8 100644 --- a/src/utils/getTranslation.ts +++ b/src/utils/getTranslation.ts @@ -1,6 +1,5 @@ import { translate } from '@vitalets/google-translate-api'; import enableCors from './enableCorsAndLimitRate'; -import getErrorInTranslationMessage from './getErrorInTranslationMessage'; import language from '../types/language'; const getTranslation = async ( @@ -8,17 +7,12 @@ const getTranslation = async ( from?: language, to?: language, ) : Promise => { - try { - // rate limit (1 request per second) and CORS policy overriding (if any) - enableCors(1); + // rate limit (1 request per second) and CORS policy overriding (if any) + enableCors(1); - // translating happens here. ✨ bing! ✨ - const translation = await translate(text as string, { from, to }); - return translation.text; - } catch (error) { - console.error(getErrorInTranslationMessage(error)); - return undefined; - } + // translating happens here. ✨ bing! ✨ + const translation = await translate(text as string, { from, to }); + return translation.text; }; export default getTranslation;