From 6c9ee67e51785d33b46d8751ed4b5acadc36543e Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Tue, 18 Jul 2023 20:20:27 -0700 Subject: [PATCH] Fixed: Improve translation loading (cherry picked from commit 73c5ec1da4dd00301e1b0dddbcea37590a99b045) Closes #3895 --- frontend/src/App/App.js | 7 +- frontend/src/Components/Page/ErrorPage.js | 8 +-- frontend/src/Components/Page/PageConnector.js | 30 ++++++--- .../Components/Page/Sidebar/PageSidebar.js | 66 +++++++++---------- .../ManageDownloadClientsEditModalContent.tsx | 22 ++++++- .../ManageDownloadClientsModalContent.tsx | 24 +++++-- .../Manage/Tags/TagsModalContent.tsx | 21 +++++- .../ManageImportListsEditModalContent.tsx | 22 ++++++- .../Manage/ManageImportListsModalContent.tsx | 24 +++++-- .../Manage/Tags/TagsModalContent.tsx | 21 +++++- .../Edit/ManageIndexersEditModalContent.tsx | 22 ++++++- .../Manage/ManageIndexersModalContent.tsx | 28 ++++++-- .../Indexers/Manage/Tags/TagsModalContent.tsx | 21 +++++- frontend/src/Store/Actions/appActions.js | 21 +++++- frontend/src/bootstrap.tsx | 8 +-- 15 files changed, 249 insertions(+), 96 deletions(-) diff --git a/frontend/src/App/App.js b/frontend/src/App/App.js index 3c1319331..3871b14e9 100644 --- a/frontend/src/App/App.js +++ b/frontend/src/App/App.js @@ -7,13 +7,13 @@ import PageConnector from 'Components/Page/PageConnector'; import ApplyTheme from './ApplyTheme'; import AppRoutes from './AppRoutes'; -function App({ store, history, hasTranslationsError }) { +function App({ store, history }) { return ( - + @@ -25,8 +25,7 @@ function App({ store, history, hasTranslationsError }) { App.propTypes = { store: PropTypes.object.isRequired, - history: PropTypes.object.isRequired, - hasTranslationsError: PropTypes.bool.isRequired + history: PropTypes.object.isRequired }; export default App; diff --git a/frontend/src/Components/Page/ErrorPage.js b/frontend/src/Components/Page/ErrorPage.js index 7c58a675b..bfb10baf4 100644 --- a/frontend/src/Components/Page/ErrorPage.js +++ b/frontend/src/Components/Page/ErrorPage.js @@ -7,7 +7,7 @@ function ErrorPage(props) { const { version, isLocalStorageSupported, - hasTranslationsError, + translationsError, artistError, customFiltersError, tagsError, @@ -21,8 +21,8 @@ function ErrorPage(props) { if (!isLocalStorageSupported) { errorMessage = 'Local Storage is not supported or disabled. A plugin or private browsing may have disabled it.'; - } else if (hasTranslationsError) { - errorMessage = 'Failed to load translations from API'; + } else if (translationsError) { + errorMessage = getErrorMessage(translationsError, 'Failed to load translations from API'); } else if (artistError) { errorMessage = getErrorMessage(artistError, 'Failed to load artist from API'); } else if (customFiltersError) { @@ -55,7 +55,7 @@ function ErrorPage(props) { ErrorPage.propTypes = { version: PropTypes.string.isRequired, isLocalStorageSupported: PropTypes.bool.isRequired, - hasTranslationsError: PropTypes.bool.isRequired, + translationsError: PropTypes.object, artistError: PropTypes.object, customFiltersError: PropTypes.object, tagsError: PropTypes.object, diff --git a/frontend/src/Components/Page/PageConnector.js b/frontend/src/Components/Page/PageConnector.js index 7340be1a2..b13695f17 100644 --- a/frontend/src/Components/Page/PageConnector.js +++ b/frontend/src/Components/Page/PageConnector.js @@ -3,7 +3,7 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; import { createSelector } from 'reselect'; -import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions'; +import { fetchTranslations, saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions'; import { fetchArtist } from 'Store/Actions/artistActions'; import { fetchCustomFilters } from 'Store/Actions/customFilterActions'; import { fetchImportLists, fetchLanguages, fetchMetadataProfiles, fetchQualityProfiles, fetchUISettings } from 'Store/Actions/settingsActions'; @@ -51,6 +51,7 @@ const selectIsPopulated = createSelector( (state) => state.settings.metadataProfiles.isPopulated, (state) => state.settings.importLists.isPopulated, (state) => state.system.status.isPopulated, + (state) => state.app.translations.isPopulated, ( customFiltersIsPopulated, tagsIsPopulated, @@ -59,7 +60,8 @@ const selectIsPopulated = createSelector( qualityProfilesIsPopulated, metadataProfilesIsPopulated, importListsIsPopulated, - systemStatusIsPopulated + systemStatusIsPopulated, + translationsIsPopulated ) => { return ( customFiltersIsPopulated && @@ -69,7 +71,8 @@ const selectIsPopulated = createSelector( qualityProfilesIsPopulated && metadataProfilesIsPopulated && importListsIsPopulated && - systemStatusIsPopulated + systemStatusIsPopulated && + translationsIsPopulated ); } ); @@ -83,6 +86,7 @@ const selectErrors = createSelector( (state) => state.settings.metadataProfiles.error, (state) => state.settings.importLists.error, (state) => state.system.status.error, + (state) => state.app.translations.error, ( customFiltersError, tagsError, @@ -91,7 +95,8 @@ const selectErrors = createSelector( qualityProfilesError, metadataProfilesError, importListsError, - systemStatusError + systemStatusError, + translationsError ) => { const hasError = !!( customFiltersError || @@ -101,7 +106,8 @@ const selectErrors = createSelector( qualityProfilesError || metadataProfilesError || importListsError || - systemStatusError + systemStatusError || + translationsError ); return { @@ -113,7 +119,8 @@ const selectErrors = createSelector( qualityProfilesError, metadataProfilesError, importListsError, - systemStatusError + systemStatusError, + translationsError }; } ); @@ -172,6 +179,9 @@ function createMapDispatchToProps(dispatch, props) { dispatchFetchStatus() { dispatch(fetchStatus()); }, + dispatchFetchTranslations() { + dispatch(fetchTranslations()); + }, onResize(dimensions) { dispatch(saveDimensions(dimensions)); }, @@ -205,6 +215,7 @@ class PageConnector extends Component { this.props.dispatchFetchImportLists(); this.props.dispatchFetchUISettings(); this.props.dispatchFetchStatus(); + this.props.dispatchFetchTranslations(); } } @@ -220,7 +231,6 @@ class PageConnector extends Component { render() { const { - hasTranslationsError, isPopulated, hasError, dispatchFetchArtist, @@ -231,15 +241,15 @@ class PageConnector extends Component { dispatchFetchImportLists, dispatchFetchUISettings, dispatchFetchStatus, + dispatchFetchTranslations, ...otherProps } = this.props; - if (hasTranslationsError || hasError || !this.state.isLocalStorageSupported) { + if (hasError || !this.state.isLocalStorageSupported) { return ( ); } @@ -260,7 +270,6 @@ class PageConnector extends Component { } PageConnector.propTypes = { - hasTranslationsError: PropTypes.bool.isRequired, isPopulated: PropTypes.bool.isRequired, hasError: PropTypes.bool.isRequired, isSidebarVisible: PropTypes.bool.isRequired, @@ -273,6 +282,7 @@ PageConnector.propTypes = { dispatchFetchImportLists: PropTypes.func.isRequired, dispatchFetchUISettings: PropTypes.func.isRequired, dispatchFetchStatus: PropTypes.func.isRequired, + dispatchFetchTranslations: PropTypes.func.isRequired, onSidebarVisibleChange: PropTypes.func.isRequired }; diff --git a/frontend/src/Components/Page/Sidebar/PageSidebar.js b/frontend/src/Components/Page/Sidebar/PageSidebar.js index 3074b7f42..583a604bd 100644 --- a/frontend/src/Components/Page/Sidebar/PageSidebar.js +++ b/frontend/src/Components/Page/Sidebar/PageSidebar.js @@ -21,24 +21,24 @@ const SIDEBAR_WIDTH = parseInt(dimensions.sidebarWidth); const links = [ { iconName: icons.ARTIST_CONTINUING, - title: translate('Library'), + title: () => translate('Library'), to: '/', alias: '/artist', children: [ { - title: translate('AddNew'), + title: () => translate('AddNew'), to: '/add/search' }, { - title: translate('MassEditor'), + title: () => translate('MassEditor'), to: '/artisteditor' }, { - title: translate('AlbumStudio'), + title: () => translate('AlbumStudio'), to: '/albumstudio' }, { - title: translate('UnmappedFiles'), + title: () => translate('UnmappedFiles'), to: '/unmapped' } ] @@ -46,26 +46,26 @@ const links = [ { iconName: icons.CALENDAR, - title: translate('Calendar'), + title: () => translate('Calendar'), to: '/calendar' }, { iconName: icons.ACTIVITY, - title: translate('Activity'), + title: () => translate('Activity'), to: '/activity/queue', children: [ { - title: translate('Queue'), + title: () => translate('Queue'), to: '/activity/queue', statusComponent: QueueStatusConnector }, { - title: translate('History'), + title: () => translate('History'), to: '/activity/history' }, { - title: translate('Blocklist'), + title: () => translate('Blocklist'), to: '/activity/blocklist' } ] @@ -73,15 +73,15 @@ const links = [ { iconName: icons.WARNING, - title: translate('Wanted'), + title: () => translate('Wanted'), to: '/wanted/missing', children: [ { - title: translate('Missing'), + title: () => translate('Missing'), to: '/wanted/missing' }, { - title: translate('CutoffUnmet'), + title: () => translate('CutoffUnmet'), to: '/wanted/cutoffunmet' } ] @@ -89,55 +89,55 @@ const links = [ { iconName: icons.SETTINGS, - title: translate('Settings'), + title: () => translate('Settings'), to: '/settings', children: [ { - title: translate('MediaManagement'), + title: () => translate('MediaManagement'), to: '/settings/mediamanagement' }, { - title: translate('Profiles'), + title: () => translate('Profiles'), to: '/settings/profiles' }, { - title: translate('Quality'), + title: () => translate('Quality'), to: '/settings/quality' }, { - title: translate('CustomFormats'), + title: () => translate('CustomFormats'), to: '/settings/customformats' }, { - title: translate('Indexers'), + title: () => translate('Indexers'), to: '/settings/indexers' }, { - title: translate('DownloadClients'), + title: () => translate('DownloadClients'), to: '/settings/downloadclients' }, { - title: translate('ImportLists'), + title: () => translate('ImportLists'), to: '/settings/importlists' }, { - title: translate('Connect'), + title: () => translate('Connect'), to: '/settings/connect' }, { - title: translate('Metadata'), + title: () => translate('Metadata'), to: '/settings/metadata' }, { - title: translate('Tags'), + title: () => translate('Tags'), to: '/settings/tags' }, { - title: translate('General'), + title: () => translate('General'), to: '/settings/general' }, { - title: translate('UI'), + title: () => translate('UI'), to: '/settings/ui' } ] @@ -145,32 +145,32 @@ const links = [ { iconName: icons.SYSTEM, - title: translate('System'), + title: () => translate('System'), to: '/system/status', children: [ { - title: translate('Status'), + title: () => translate('Status'), to: '/system/status', statusComponent: HealthStatusConnector }, { - title: translate('Tasks'), + title: () => translate('Tasks'), to: '/system/tasks' }, { - title: translate('Backup'), + title: () => translate('Backup'), to: '/system/backup' }, { - title: translate('Updates'), + title: () => translate('Updates'), to: '/system/updates' }, { - title: translate('Events'), + title: () => translate('Events'), to: '/system/events' }, { - title: translate('LogFiles'), + title: () => translate('LogFiles'), to: '/system/logs/files' } ] diff --git a/frontend/src/Settings/DownloadClients/DownloadClients/Manage/Edit/ManageDownloadClientsEditModalContent.tsx b/frontend/src/Settings/DownloadClients/DownloadClients/Manage/Edit/ManageDownloadClientsEditModalContent.tsx index e719a6907..18ae5170a 100644 --- a/frontend/src/Settings/DownloadClients/DownloadClients/Manage/Edit/ManageDownloadClientsEditModalContent.tsx +++ b/frontend/src/Settings/DownloadClients/DownloadClients/Manage/Edit/ManageDownloadClientsEditModalContent.tsx @@ -27,9 +27,25 @@ interface ManageDownloadClientsEditModalContentProps { const NO_CHANGE = 'noChange'; const enableOptions = [ - { key: NO_CHANGE, value: translate('NoChange'), disabled: true }, - { key: 'enabled', value: translate('Enabled') }, - { key: 'disabled', value: translate('Disabled') }, + { + key: NO_CHANGE, + get value() { + return translate('NoChange'); + }, + disabled: true, + }, + { + key: 'enabled', + get value() { + return translate('Enabled'); + }, + }, + { + key: 'disabled', + get value() { + return translate('Disabled'); + }, + }, ]; function ManageDownloadClientsEditModalContent( diff --git a/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.tsx b/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.tsx index 5d8f6dd49..3700798ee 100644 --- a/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.tsx +++ b/frontend/src/Settings/DownloadClients/DownloadClients/Manage/ManageDownloadClientsModalContent.tsx @@ -36,37 +36,49 @@ type OnSelectedChangeCallback = React.ComponentProps< const COLUMNS = [ { name: 'name', - label: translate('Name'), + get label() { + return translate('Name'); + }, isSortable: true, isVisible: true, }, { name: 'implementation', - label: translate('Implementation'), + get label() { + return translate('Implementation'); + }, isSortable: true, isVisible: true, }, { name: 'enable', - label: translate('Enabled'), + get label() { + return translate('Enabled'); + }, isSortable: true, isVisible: true, }, { name: 'priority', - label: translate('Priority'), + get label() { + return translate('Priority'); + }, isSortable: true, isVisible: true, }, { name: 'removeCompletedDownloads', - label: translate('RemoveCompleted'), + get label() { + return translate('RemoveCompleted'); + }, isSortable: true, isVisible: true, }, { name: 'removeFailedDownloads', - label: translate('RemoveFailed'), + get label() { + return translate('RemoveFailed'); + }, isSortable: true, isVisible: true, }, diff --git a/frontend/src/Settings/DownloadClients/DownloadClients/Manage/Tags/TagsModalContent.tsx b/frontend/src/Settings/DownloadClients/DownloadClients/Manage/Tags/TagsModalContent.tsx index 98f33c109..e0899ff39 100644 --- a/frontend/src/Settings/DownloadClients/DownloadClients/Manage/Tags/TagsModalContent.tsx +++ b/frontend/src/Settings/DownloadClients/DownloadClients/Manage/Tags/TagsModalContent.tsx @@ -72,9 +72,24 @@ function TagsModalContent(props: TagsModalContentProps) { }, [tags, applyTags, onApplyTagsPress]); const applyTagsOptions = [ - { key: 'add', value: translate('Add') }, - { key: 'remove', value: translate('Remove') }, - { key: 'replace', value: translate('Replace') }, + { + key: 'add', + get value() { + return translate('Add'); + }, + }, + { + key: 'remove', + get value() { + return translate('Remove'); + }, + }, + { + key: 'replace', + get value() { + return translate('Replace'); + }, + }, ]; return ( diff --git a/frontend/src/Settings/ImportLists/ImportLists/Manage/Edit/ManageImportListsEditModalContent.tsx b/frontend/src/Settings/ImportLists/ImportLists/Manage/Edit/ManageImportListsEditModalContent.tsx index 477cbfef3..5a651ba28 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/Manage/Edit/ManageImportListsEditModalContent.tsx +++ b/frontend/src/Settings/ImportLists/ImportLists/Manage/Edit/ManageImportListsEditModalContent.tsx @@ -26,9 +26,25 @@ interface ManageImportListsEditModalContentProps { const NO_CHANGE = 'noChange'; const autoAddOptions = [ - { key: NO_CHANGE, value: translate('NoChange'), disabled: true }, - { key: 'enabled', value: translate('Enabled') }, - { key: 'disabled', value: translate('Disabled') }, + { + key: NO_CHANGE, + get value() { + return translate('NoChange'); + }, + disabled: true, + }, + { + key: 'enabled', + get value() { + return translate('Enabled'); + }, + }, + { + key: 'disabled', + get value() { + return translate('Disabled'); + }, + }, ]; function ManageImportListsEditModalContent( diff --git a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.tsx b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.tsx index fdcc20f8b..5d78e38d2 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.tsx +++ b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.tsx @@ -36,37 +36,49 @@ type OnSelectedChangeCallback = React.ComponentProps< const COLUMNS = [ { name: 'name', - label: translate('Name'), + get label() { + return translate('Name'); + }, isSortable: true, isVisible: true, }, { name: 'implementation', - label: translate('Implementation'), + get label() { + return translate('Implementation'); + }, isSortable: true, isVisible: true, }, { name: 'qualityProfileId', - label: translate('QualityProfile'), + get label() { + return translate('QualityProfile'); + }, isSortable: true, isVisible: true, }, { name: 'rootFolderPath', - label: translate('RootFolder'), + get label() { + return translate('RootFolder'); + }, isSortable: true, isVisible: true, }, { name: 'enableAutomaticAdd', - label: translate('AutoAdd'), + get label() { + return translate('AutoAdd'); + }, isSortable: true, isVisible: true, }, { name: 'tags', - label: translate('Tags'), + get label() { + return translate('Tags'); + }, isSortable: true, isVisible: true, }, diff --git a/frontend/src/Settings/ImportLists/ImportLists/Manage/Tags/TagsModalContent.tsx b/frontend/src/Settings/ImportLists/ImportLists/Manage/Tags/TagsModalContent.tsx index 6072be5ff..9d4af820e 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/Manage/Tags/TagsModalContent.tsx +++ b/frontend/src/Settings/ImportLists/ImportLists/Manage/Tags/TagsModalContent.tsx @@ -70,9 +70,24 @@ function TagsModalContent(props: TagsModalContentProps) { }, [tags, applyTags, onApplyTagsPress]); const applyTagsOptions = [ - { key: 'add', value: translate('Add') }, - { key: 'remove', value: translate('Remove') }, - { key: 'replace', value: translate('Replace') }, + { + key: 'add', + get value() { + return translate('Add'); + }, + }, + { + key: 'remove', + get value() { + return translate('Remove'); + }, + }, + { + key: 'replace', + get value() { + return translate('Replace'); + }, + }, ]; return ( diff --git a/frontend/src/Settings/Indexers/Indexers/Manage/Edit/ManageIndexersEditModalContent.tsx b/frontend/src/Settings/Indexers/Indexers/Manage/Edit/ManageIndexersEditModalContent.tsx index dd76376d6..f9b051986 100644 --- a/frontend/src/Settings/Indexers/Indexers/Manage/Edit/ManageIndexersEditModalContent.tsx +++ b/frontend/src/Settings/Indexers/Indexers/Manage/Edit/ManageIndexersEditModalContent.tsx @@ -27,9 +27,25 @@ interface ManageIndexersEditModalContentProps { const NO_CHANGE = 'noChange'; const enableOptions = [ - { key: NO_CHANGE, value: translate('NoChange'), disabled: true }, - { key: 'enabled', value: translate('Enabled') }, - { key: 'disabled', value: translate('Disabled') }, + { + key: NO_CHANGE, + get value() { + return translate('NoChange'); + }, + disabled: true, + }, + { + key: 'enabled', + get value() { + return translate('Enabled'); + }, + }, + { + key: 'disabled', + get value() { + return translate('Disabled'); + }, + }, ]; function ManageIndexersEditModalContent( diff --git a/frontend/src/Settings/Indexers/Indexers/Manage/ManageIndexersModalContent.tsx b/frontend/src/Settings/Indexers/Indexers/Manage/ManageIndexersModalContent.tsx index 20418c682..da24b4412 100644 --- a/frontend/src/Settings/Indexers/Indexers/Manage/ManageIndexersModalContent.tsx +++ b/frontend/src/Settings/Indexers/Indexers/Manage/ManageIndexersModalContent.tsx @@ -36,43 +36,57 @@ type OnSelectedChangeCallback = React.ComponentProps< const COLUMNS = [ { name: 'name', - label: translate('Name'), + get label() { + return translate('Name'); + }, isSortable: true, isVisible: true, }, { name: 'implementation', - label: translate('Implementation'), + get label() { + return translate('Implementation'); + }, isSortable: true, isVisible: true, }, { name: 'enableRss', - label: translate('EnableRSS'), + get label() { + return translate('EnableRSS'); + }, isSortable: true, isVisible: true, }, { name: 'enableAutomaticSearch', - label: translate('EnableAutomaticSearch'), + get label() { + return translate('EnableAutomaticSearch'); + }, isSortable: true, isVisible: true, }, { name: 'enableInteractiveSearch', - label: translate('EnableInteractiveSearch'), + get label() { + return translate('EnableInteractiveSearch'); + }, isSortable: true, isVisible: true, }, { name: 'priority', - label: translate('Priority'), + get label() { + return translate('Priority'); + }, isSortable: true, isVisible: true, }, { name: 'tags', - label: translate('Tags'), + get label() { + return translate('Tags'); + }, isSortable: true, isVisible: true, }, diff --git a/frontend/src/Settings/Indexers/Indexers/Manage/Tags/TagsModalContent.tsx b/frontend/src/Settings/Indexers/Indexers/Manage/Tags/TagsModalContent.tsx index 32138fb5f..fb1e6b847 100644 --- a/frontend/src/Settings/Indexers/Indexers/Manage/Tags/TagsModalContent.tsx +++ b/frontend/src/Settings/Indexers/Indexers/Manage/Tags/TagsModalContent.tsx @@ -70,9 +70,24 @@ function TagsModalContent(props: TagsModalContentProps) { }, [tags, applyTags, onApplyTagsPress]); const applyTagsOptions = [ - { key: 'add', value: translate('Add') }, - { key: 'remove', value: translate('Remove') }, - { key: 'replace', value: translate('Replace') }, + { + key: 'add', + get value() { + return translate('Add'); + }, + }, + { + key: 'remove', + get value() { + return translate('Remove'); + }, + }, + { + key: 'replace', + get value() { + return translate('Replace'); + }, + }, ]; return ( diff --git a/frontend/src/Store/Actions/appActions.js b/frontend/src/Store/Actions/appActions.js index 082bd1dc7..972fd1be2 100644 --- a/frontend/src/Store/Actions/appActions.js +++ b/frontend/src/Store/Actions/appActions.js @@ -4,6 +4,7 @@ import { createThunk, handleThunks } from 'Store/thunks'; import createAjaxRequest from 'Utilities/createAjaxRequest'; import getSectionState from 'Utilities/State/getSectionState'; import updateSectionState from 'Utilities/State/updateSectionState'; +import { fetchTranslations as fetchAppTranslations } from 'Utilities/String/translate'; import createHandleActions from './Creators/createHandleActions'; function getDimensions(width, height) { @@ -41,7 +42,12 @@ export const defaultState = { isReconnecting: false, isDisconnected: false, isRestarting: false, - isSidebarVisible: !getDimensions(window.innerWidth, window.innerHeight).isSmallScreen + isSidebarVisible: !getDimensions(window.innerWidth, window.innerHeight).isSmallScreen, + translations: { + isFetching: true, + isPopulated: false, + error: null + } }; // @@ -53,6 +59,7 @@ export const SAVE_DIMENSIONS = 'app/saveDimensions'; export const SET_VERSION = 'app/setVersion'; export const SET_APP_VALUE = 'app/setAppValue'; export const SET_IS_SIDEBAR_VISIBLE = 'app/setIsSidebarVisible'; +export const FETCH_TRANSLATIONS = 'app/fetchTranslations'; export const PING_SERVER = 'app/pingServer'; @@ -66,6 +73,7 @@ export const setAppValue = createAction(SET_APP_VALUE); export const showMessage = createAction(SHOW_MESSAGE); export const hideMessage = createAction(HIDE_MESSAGE); export const pingServer = createThunk(PING_SERVER); +export const fetchTranslations = createThunk(FETCH_TRANSLATIONS); // // Helpers @@ -127,6 +135,17 @@ function pingServerAfterTimeout(getState, dispatch) { export const actionHandlers = handleThunks({ [PING_SERVER]: function(getState, payload, dispatch) { pingServerAfterTimeout(getState, dispatch); + }, + [FETCH_TRANSLATIONS]: async function(getState, payload, dispatch) { + const isFetchingComplete = await fetchAppTranslations(); + + dispatch(setAppValue({ + translations: { + isFetching: false, + isPopulated: isFetchingComplete, + error: isFetchingComplete ? null : 'Failed to load translations from API' + } + })); } }); diff --git a/frontend/src/bootstrap.tsx b/frontend/src/bootstrap.tsx index a729cb3c5..6a6d7fc67 100644 --- a/frontend/src/bootstrap.tsx +++ b/frontend/src/bootstrap.tsx @@ -2,7 +2,6 @@ import { createBrowserHistory } from 'history'; import React from 'react'; import { render } from 'react-dom'; import createAppStore from 'Store/createAppStore'; -import { fetchTranslations } from 'Utilities/String/translate'; import App from './App/App'; import 'Diag/ConsoleApi'; @@ -10,14 +9,9 @@ import 'Diag/ConsoleApi'; export async function bootstrap() { const history = createBrowserHistory(); const store = createAppStore(history); - const hasTranslationsError = !(await fetchTranslations()); render( - , + , document.getElementById('root') ); }