From 872d5659a222b478747998c8fb22aeb54b17039a Mon Sep 17 00:00:00 2001 From: Sam May Date: Wed, 7 Jun 2023 23:03:52 -0400 Subject: [PATCH 1/5] feat(localization): create localization types --- src/schema/localizationTypes.ts | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/schema/localizationTypes.ts diff --git a/src/schema/localizationTypes.ts b/src/schema/localizationTypes.ts new file mode 100644 index 0000000..88078b3 --- /dev/null +++ b/src/schema/localizationTypes.ts @@ -0,0 +1,41 @@ +export enum LanguageCode { + enUS = 'en-us', + es = 'es', + uk = 'uk', +} + +export type LanguageCodeKeys = keyof typeof LanguageCode + +export type AppStringValues = { + instructions_title: string + instructions_1: string + instructions_2: string + instructions_3: string + instructions_4: string + instructions_5: string + instructions_button: string +} + +type AppStringEntry = { + language: string + appStringValues: AppStringValues +} + +export const appStrings = new Map() +appStrings.set('enUS', { + language: 'English', + appStringValues: { + instructions_title: 'Instructions', + instructions_1: + 'Please fill out each question to the best of your ability, and submit the survey before the beginning of the quarter you are reporting your needs for.', + instructions_2: + "Please only consider your organisation's total needs for the quarter in question that you don't already have covered.", + instructions_3: + 'It is okay to use estimates, and we understand that conditions & needs change! We are only looking for a rough understanding of your needs.', + instructions_4: + 'If your organisation operates in multiple regions, please fill out the survey multiple times. Please submit it once per region that you operate in, so we can keep the needs data separate.', + instructions_5: + 'If you want to submit an assessment for a different quarter, please create a separate submission.', + instructions_button: 'Continue', + }, +}) From 9d9dd26a6dc8f193c98a62ac758f8b9051e5fd06 Mon Sep 17 00:00:00 2001 From: Sam May Date: Wed, 7 Jun 2023 23:13:29 -0400 Subject: [PATCH 2/5] feat(localization): create localization hook --- src/hooks/useLocalization.tsx | 47 ++++++++++++++++++++++++++++++++++ src/utils/getAppStringValue.ts | 25 ++++++++++++++++++ tsconfig.json | 1 - 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/hooks/useLocalization.tsx create mode 100644 src/utils/getAppStringValue.ts diff --git a/src/hooks/useLocalization.tsx b/src/hooks/useLocalization.tsx new file mode 100644 index 0000000..66e5b9d --- /dev/null +++ b/src/hooks/useLocalization.tsx @@ -0,0 +1,47 @@ +import { + createContext, + FunctionComponent, + ReactNode, + useCallback, + useContext, + useState, +} from 'react' +import { AppStringValues, LanguageCodeKeys } from 'schema/localizationTypes' +import { getAppStringValue } from 'utils/getAppStringValue' + +export const LocalizationContext = createContext<{ + languageCode?: LanguageCodeKeys + setLanguageCode: (languageCode: LanguageCodeKeys) => void + localizeAppString: (appStringKey: keyof AppStringValues) => string +}>({ + setLanguageCode: () => undefined, + localizeAppString: (_appStringKey) => { + throw new Error("localizeAppString called before it's implemented") + }, +}) + +export const useLocalizationContext = () => useContext(LocalizationContext) + +export const LocalizationProvider: FunctionComponent<{ + children: ReactNode +}> = ({ children }) => { + const [languageCode, setLanguageCode] = useState('enUS') + const localizeAppString = useCallback( + (appStringKey: keyof AppStringValues) => { + return getAppStringValue(languageCode, appStringKey) + }, + [languageCode], + ) + + return ( + + {children} + + ) +} diff --git a/src/utils/getAppStringValue.ts b/src/utils/getAppStringValue.ts new file mode 100644 index 0000000..7868744 --- /dev/null +++ b/src/utils/getAppStringValue.ts @@ -0,0 +1,25 @@ +import { + appStrings, + AppStringValues, + LanguageCodeKeys, +} from 'schema/localizationTypes' + +export const getAppStringValue = ( + languageCode: LanguageCodeKeys, + appStringKey: keyof AppStringValues, +): string => { + const appStringEntry = appStrings.get(languageCode) + + if (!appStringEntry) { + throw new Error('Language code not supported') + } + + const appStringValue = + appStringEntry.appStringValues[appStringKey] ?? appStringEntry + + if (!appStringValue) { + throw new Error('Invalid app string key provided') + } + + return appStringValue +} diff --git a/tsconfig.json b/tsconfig.json index a007cd0..881b3dc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,6 @@ "noEmit": true, "jsx": "preserve", "noFallthroughCasesInSwitch": true, - "importsNotUsedAsValues": "error", "baseUrl": "src", "paths": { "app/*": ["app/*"], From f8a16d3947c1d6c775c1fd1886d177cf33f7e9a7 Mon Sep 17 00:00:00 2001 From: Sam May Date: Wed, 7 Jun 2023 23:16:55 -0400 Subject: [PATCH 3/5] feat(localization): localize instructions page --- src/app/App.tsx | 56 ++++++++++++++++------------- src/app/pages/Instructions.tsx | 64 +++++++++++++--------------------- 2 files changed, 56 insertions(+), 64 deletions(-) diff --git a/src/app/App.tsx b/src/app/App.tsx index 25aecb4..6e6e7ba 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -15,6 +15,7 @@ import { useAppConfig } from 'hooks/useAppConfig' import { AuthProvider } from 'hooks/useAuth' import { CorrectionProvider } from 'hooks/useCorrection' import { FormProvider } from 'hooks/useForm' +import { LocalizationProvider } from 'hooks/useLocalization' import { ResponseProvider } from 'hooks/useResponse' import { StoredFormProvider } from 'hooks/useStoredForm' import { BrowserRouter as Router, Route, Routes } from 'react-router-dom' @@ -23,31 +24,36 @@ export const App = () => { const { basename } = useAppConfig() return ( - - - - - - - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - - - - - - + + + + + + + + + } /> + } /> + } /> + } + /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + + + + + + ) } diff --git a/src/app/pages/Instructions.tsx b/src/app/pages/Instructions.tsx index 653bfc1..abe3ae4 100644 --- a/src/app/pages/Instructions.tsx +++ b/src/app/pages/Instructions.tsx @@ -1,42 +1,28 @@ +import { useLocalizationContext } from 'hooks/useLocalization' import { Link } from 'react-router-dom' -export const Instructions = () => ( -
-
-
-

Instructions

-
    -
  1. - Please fill out each question to the best of your ability, and - submit the survey before the beginning of the quarter you are - reporting your needs for. -
  2. -
  3. - Please only consider your organisation's total needs for the quarter - in question that you don't already have covered. -
  4. -
  5. - It is okay to use estimates, and we understand that conditions & - needs change! We are only looking for a rough understanding of your - needs. -
  6. -
  7. - If your organisation operates in multiple regions, please fill out - the survey multiple times. Please submit it once per region that you - operate in, so we can keep the needs data separate. -
  8. -
  9. - If you want to submit an assessment for a different quarter, please - create a separate submission. -
  10. -
+export const Instructions = () => { + const { localizeAppString } = useLocalizationContext() + return ( +
+
+
+

{localizeAppString('instructions_title')}

+
    +
  1. {localizeAppString('instructions_1')}
  2. +
  3. {localizeAppString('instructions_2')}
  4. +
  5. {localizeAppString('instructions_3')}
  6. +
  7. {localizeAppString('instructions_4')}
  8. +
  9. {localizeAppString('instructions_5')}
  10. +
-

- - Continue - -

-
-
-
-) +

+ + {localizeAppString('instructions_button')} + +

+
+
+
+ ) +} From cbf9464878ee59924db256e73fbc13a5446ab6e4 Mon Sep 17 00:00:00 2001 From: Sam May Date: Thu, 13 Jul 2023 22:29:18 -0400 Subject: [PATCH 4/5] feat(localization): create language selector component --- src/components/LanguageSelector.tsx | 34 +++++++++++++++++++++++++++++ src/components/Navbar.tsx | 4 ++++ 2 files changed, 38 insertions(+) create mode 100644 src/components/LanguageSelector.tsx diff --git a/src/components/LanguageSelector.tsx b/src/components/LanguageSelector.tsx new file mode 100644 index 0000000..9ce9171 --- /dev/null +++ b/src/components/LanguageSelector.tsx @@ -0,0 +1,34 @@ +import { useLocalizationContext } from 'hooks/useLocalization' +import { FunctionComponent } from 'react' +import { appStrings, LanguageCodeKeys } from 'schema/localizationTypes' + +type LangValuesForDropdown = { + code: LanguageCodeKeys + displayName: string +} + +export const LanguageSelector: FunctionComponent = () => { + const { languageCode, setLanguageCode } = useLocalizationContext() + const languageValues: LangValuesForDropdown[] = Array.from( + appStrings.entries(), + ).map(([langKey, appStringEntry]) => ({ + code: langKey, + displayName: appStringEntry.language, + })) + + return ( + + ) +} diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 4541245..a254873 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,4 +1,5 @@ import { ExpiresCountdown } from 'components/ExpiresCountdown' +import { LanguageSelector } from 'components/LanguageSelector' import { useAppConfig } from 'hooks/useAppConfig' import { useAuth } from 'hooks/useAuth' import { useState } from 'react' @@ -102,6 +103,9 @@ export const Navbar = () => {