Skip to content

Commit

Permalink
chore: better error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
MiracleUFO committed Sep 16, 2023
1 parent ce79f63 commit af9549b
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 75 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <Wrapper /> 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: `<Translator />` ###
*Type:* React functional component
Expand Down Expand Up @@ -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*


## 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.
60 changes: 42 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
13 changes: 0 additions & 13 deletions src/components/Translate.tsx
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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 (
<QueryClientProvider client={queryClient}>
<LanguageProvider>
Expand Down
11 changes: 10 additions & 1 deletion src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
13 changes: 12 additions & 1 deletion src/queries/useTranslation.ts
Original file line number Diff line number Diff line change
@@ -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 = (
Expand All @@ -14,7 +16,16 @@ const useTranslation = (
error,
isError,
isLoading,
} = useQuery<string | undefined>('translation', () => getTranslation(text, from || languageFrom, to || languageTo));
} = useQuery<string | undefined>(
'translation',
() => getTranslation(text, from || languageFrom, to || languageTo),
{
// more descriptive error than react-query error
onError: (err: unknown) => {
console.error(getErrorInTranslationMessage(err));
},
},
);

return {
data,
Expand Down
18 changes: 3 additions & 15 deletions src/scripts/getTranslation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,12 @@ const getTranslation = async (
text: string,
from = DEFAULT_LANGUAGE_FROM,
to = DEFAULT_BROWSER_LANGUAGE,
shouldFallback = true,
) : Promise<string> => {
// 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);
}
Expand Down
13 changes: 3 additions & 10 deletions src/tests/utils-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}) => (
<QueryClientProvider client={queryClient}>
Expand All @@ -18,14 +20,5 @@ const customRender = (
options?: Omit<RenderOptions, 'wrapper'>,
) => 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,
};
2 changes: 1 addition & 1 deletion src/utils/determineRenderedText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 '';
};

Expand Down
2 changes: 1 addition & 1 deletion src/utils/getErrorInTranslationMessage.ts
Original file line number Diff line number Diff line change
@@ -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;
16 changes: 5 additions & 11 deletions src/utils/getTranslation.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
import { translate } from '@vitalets/google-translate-api';
import enableCors from './enableCorsAndLimitRate';
import getErrorInTranslationMessage from './getErrorInTranslationMessage';
import language from '../types/language';

const getTranslation = async (
text: string | string[],
from?: language,
to?: language,
) : Promise<string | undefined> => {
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;

0 comments on commit af9549b

Please sign in to comment.