diff --git a/src/about/AboutDialog.tsx b/src/about/AboutDialog.tsx index 725e5e4f..76206bf8 100644 --- a/src/about/AboutDialog.tsx +++ b/src/about/AboutDialog.tsx @@ -5,7 +5,6 @@ import { AnchorButton, Button, Classes, Dialog } from '@blueprintjs/core'; import { firmwareVersion } from '@pybricks/firmware'; -import { useI18n } from '@shopify/react-i18n'; import React, { useState } from 'react'; import { appName, @@ -17,7 +16,7 @@ import { } from '../app/constants'; import LicenseDialog from '../licenses/LicenseDialog'; import ExternalLinkIcon from '../utils/ExternalLinkIcon'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; import './about.scss'; @@ -29,8 +28,7 @@ const AboutDialog: React.VoidFunctionComponent = ({ }) => { const [isLicenseDialogOpen, setIsLicenseDialogOpen] = useState(false); - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); return ( { - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); const [selectedActivity, setSelectedActivity] = useLocalStorage( 'activities.selectedActivity', diff --git a/src/activities/i18n.ts b/src/activities/i18n.ts index 5d4ea7ef..703906be 100644 --- a/src/activities/i18n.ts +++ b/src/activities/i18n.ts @@ -3,6 +3,14 @@ // // Explorer translation keys. +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { Title = 'title', Explorer = 'explorer', diff --git a/src/alerts/UnexpectedErrorAlert.tsx b/src/alerts/UnexpectedErrorAlert.tsx index f1938484..ebbf2978 100644 --- a/src/alerts/UnexpectedErrorAlert.tsx +++ b/src/alerts/UnexpectedErrorAlert.tsx @@ -3,11 +3,10 @@ import './UnexpectedErrorAlert.scss'; import { AnchorButton, Button, ButtonGroup, Collapse, Intent } from '@blueprintjs/core'; -import { useI18n } from '@shopify/react-i18n'; import React, { useState } from 'react'; import { useId } from 'react-aria'; import { CreateToast } from '../i18nToaster'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; type UnexpectedErrorAlertProps = { error: Error; @@ -16,8 +15,7 @@ type UnexpectedErrorAlertProps = { const UnexpectedErrorAlert: React.VoidFunctionComponent = ({ error, }) => { - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); const [isExpanded, setIsExpanded] = useState(false); const labelId = useId(); diff --git a/src/alerts/i18n.ts b/src/alerts/i18n.ts index f6d2a72e..773d6e0f 100644 --- a/src/alerts/i18n.ts +++ b/src/alerts/i18n.ts @@ -1,6 +1,14 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2022 The Pybricks Authors +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { Message = 'message', TechnicalInfo = 'technicalInfo', diff --git a/src/components/HelpButton.tsx b/src/components/HelpButton.tsx index 2d911dae..8235a7e6 100644 --- a/src/components/HelpButton.tsx +++ b/src/components/HelpButton.tsx @@ -2,13 +2,12 @@ // Copyright (c) 2022 The Pybricks Authors import { Classes } from '@blueprintjs/core'; -import { useI18n } from '@shopify/react-i18n'; import React, { useCallback, useEffect, useState } from 'react'; import { OverlayContainer } from 'react-aria'; import { useBoolean } from 'usehooks-ts'; import { Button } from './Button'; import HelpDialog from './HelpDialog'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; type HelpButtonProps = { /** The label of the control this button provides help for. */ @@ -21,8 +20,7 @@ const HelpButton: React.VoidFunctionComponent = ({ helpForLabel, content, }) => { - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); const { value: isDialogOpen, diff --git a/src/components/HelpDialog.tsx b/src/components/HelpDialog.tsx index b12611f1..8cd78e71 100644 --- a/src/components/HelpDialog.tsx +++ b/src/components/HelpDialog.tsx @@ -8,7 +8,6 @@ import { POPOVER_ARROW_SVG_SIZE, Popover2Arrow, } from '@blueprintjs/popover2/lib/cjs/popover2Arrow'; -import { useI18n } from '@shopify/react-i18n'; import classNames from 'classnames'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import { @@ -21,7 +20,7 @@ import { useOverlay, } from 'react-aria'; import { usePopper } from 'react-popper'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; type HelpDialogProps = { /** The title of the dialog. */ @@ -45,8 +44,7 @@ const HelpDialog: React.FunctionComponent = ({ onAnimationEnd, children, }) => { - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); // this is the dialog element and the popper element const ref = useRef(null); diff --git a/src/components/i18n.ts b/src/components/i18n.ts index 50636f48..48784536 100644 --- a/src/components/i18n.ts +++ b/src/components/i18n.ts @@ -1,6 +1,14 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2022 The Pybricks Authors +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { HelpButtonLabel = 'helpButton.label', HelpButtonDescription = 'helpButton.description', diff --git a/src/editor/Editor.tsx b/src/editor/Editor.tsx index 9dbe45c5..bc012945 100644 --- a/src/editor/Editor.tsx +++ b/src/editor/Editor.tsx @@ -16,7 +16,6 @@ import { Text, } from '@blueprintjs/core'; import { ContextMenu2, ResizeSensor2 } from '@blueprintjs/popover2'; -import { I18n, useI18n } from '@shopify/react-i18n'; import tomorrowNightEightiesTheme from 'monaco-themes/themes/Tomorrow-Night-Eighties.json'; import xcodeTheme from 'monaco-themes/themes/Xcode_default.json'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; @@ -34,7 +33,7 @@ import { useSelector } from '../reducers'; import { useSettingIsShowDocsEnabled } from '../settings/hooks'; import { isMacOS } from '../utils/os'; import { editorActivateFile, editorCloseFile } from './actions'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; import * as pybricksMicroPython from './pybricksMicroPython'; import { pybricksMicroPythonId } from './pybricksMicroPython'; import { UntitledHintContribution } from './untitledHint'; @@ -114,14 +113,12 @@ const EditorContextMenuItem: React.VoidFunctionComponent< type EditorContextMenuProps = { /** The editor. */ editor: monaco.editor.IStandaloneCodeEditor | undefined; - /** Translation context. */ - i18n: I18n; }; const EditorContextMenu: React.VoidFunctionComponent = ({ editor, - i18n, }) => { + const i18n = useI18n(); const selection = editor?.getSelection(); const hasSelection = selection && !selection.isEmpty(); @@ -179,17 +176,13 @@ const EditorContextMenu: React.VoidFunctionComponent = ( type EditorTabsProps = Readonly<{ /** Called when the selected tab changes. */ onChange?: () => void; - /** Translation context. */ - i18n: I18n; }>; -const EditorTabs: React.VoidFunctionComponent = ({ - onChange, - i18n, -}) => { +const EditorTabs: React.VoidFunctionComponent = ({ onChange }) => { const openFiles = useSelector((s) => s.editor.openFiles); const activeFile = useSelector((s) => s.editor.activeFile); const dispatch = useDispatch(); + const i18n = useI18n(); const handleChange = useCallback( (newTabId: TabId) => { @@ -301,8 +294,7 @@ const Editor: React.VFC = () => { const { toggleIsSettingShowDocsEnabled } = useSettingIsShowDocsEnabled(); const { isDarkMode } = useTernaryDarkMode(); - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); const options = useMemo( () => ({ @@ -423,7 +415,7 @@ const Editor: React.VFC = () => { return (
- editor?.focus()} i18n={i18n} /> + editor?.focus()} /> editor?.layout()}> { // NB: we have to create a new context menu each time it is // shown in order to get some state, like canUndo and canRedo // that don't have events to monitor changes. - content={() => } + content={() => } popoverProps={popoverProps} > ; type ActionButtonGroupProps = { /** The name of the file (displayed to user) */ item: TreeItem; - /** Translation context. */ - i18n: I18n; }; const FileActionButtonGroup: React.VoidFunctionComponent = ({ item, - i18n, }) => { + const i18n = useI18n(); const dispatch = useDispatch(); const environment = useTreeEnvironment(); @@ -145,16 +142,12 @@ const FileActionButtonGroup: React.VoidFunctionComponent ); }; -type HeaderProps = { - /** Translation context. */ - i18n: I18n; -}; - -const Header: React.VoidFunctionComponent = ({ i18n }) => { +const Header: React.VoidFunctionComponent = () => { const archiveButtonId = useId(); const exportButtonId = useId(); const newButtonId = useId(); const dispatch = useDispatch(); + const i18n = useI18n(); return ( = ({ i18n }) => { /** * Accessibility live descriptors. - * @param i18n Translation context. */ -function useLiveDescriptors(i18n: I18n): LiveDescriptors { +function useLiveDescriptors(): LiveDescriptors { + const i18n = useI18n(); + return useMemo( () => ({ introduction: ` @@ -301,15 +295,10 @@ const renderTreeContainer: typeof renderers.renderTreeContainer = (props) => { return
{renderers.renderTreeContainer(props)}
; }; -type FileTreeProps = { - /** Translation context. */ - i18n: I18n; -}; - -const FileTree: React.VoidFunctionComponent = ({ i18n }) => { +const FileTree: React.VoidFunctionComponent = () => { const [focusedItem, setFocusedItem] = useState(); const files = useFileStorageMetadata() ?? []; - const liveDescriptors = useLiveDescriptors(i18n); + const liveDescriptors = useLiveDescriptors(); const rootItemIndex = 'root'; @@ -326,12 +315,7 @@ const FileTree: React.VoidFunctionComponent = ({ i18n }) => { icon: 'document', secondaryLabel: ( - {(item) => ( - - )} + {(item) => } ), }, @@ -348,7 +332,7 @@ const FileTree: React.VoidFunctionComponent = ({ i18n }) => { }, } as Record, ), - [i18n, files], + [files], ); const getItemTitle = useCallback((item: FileTreeItem) => item.data.fileName, []); @@ -361,6 +345,7 @@ const FileTree: React.VoidFunctionComponent = ({ i18n }) => { ); const dispatch = useDispatch(); + const i18n = useI18n(); return ( @@ -388,14 +373,11 @@ const FileTree: React.VoidFunctionComponent = ({ i18n }) => { }; const Explorer: React.VFC = () => { - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); - return (
-
+
- + diff --git a/src/explorer/alerts/FileInUseAlert.tsx b/src/explorer/alerts/FileInUseAlert.tsx index b4da1782..4719c654 100644 --- a/src/explorer/alerts/FileInUseAlert.tsx +++ b/src/explorer/alerts/FileInUseAlert.tsx @@ -2,10 +2,9 @@ // Copyright (c) 2022 The Pybricks Authors import { Intent } from '@blueprintjs/core'; -import { useI18n } from '@shopify/react-i18n'; import React from 'react'; import { CreateToast } from '../../i18nToaster'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; type FileInUseAlertProps = { fileName: string; @@ -14,8 +13,7 @@ type FileInUseAlertProps = { const FileInUseAlert: React.VoidFunctionComponent = ({ fileName, }) => { - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); return <>{i18n.translate(I18nId.FileInUseMessage, { fileName })}; }; diff --git a/src/explorer/alerts/i18n.ts b/src/explorer/alerts/i18n.ts index 8df7b63b..40d331a8 100644 --- a/src/explorer/alerts/i18n.ts +++ b/src/explorer/alerts/i18n.ts @@ -1,6 +1,14 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2022 The Pybricks Authors +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { FileInUseMessage = 'fileInUse.message', } diff --git a/src/explorer/deleteFileAlert/DeleteFileAlert.tsx b/src/explorer/deleteFileAlert/DeleteFileAlert.tsx index 7c35be81..06879e42 100644 --- a/src/explorer/deleteFileAlert/DeleteFileAlert.tsx +++ b/src/explorer/deleteFileAlert/DeleteFileAlert.tsx @@ -2,18 +2,16 @@ // Copyright (c) 2022 The Pybricks Authors import { Alert, Classes, Intent } from '@blueprintjs/core'; -import { useI18n } from '@shopify/react-i18n'; import React, { useCallback } from 'react'; import { useDispatch } from 'react-redux'; import { useSelector } from '../../reducers'; import { deleteFileAlertDidAccept, deleteFileAlertDidCancel } from './actions'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; const DeleteFileAlert: React.VoidFunctionComponent = () => { const { isOpen, fileName } = useSelector((s) => s.explorer.deleteFileAlert); const dispatch = useDispatch(); - // istanbul ignore next: babel rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); // a11y: focus primary button when dialog is opened const handleOpened = useCallback((node: HTMLElement) => { diff --git a/src/explorer/deleteFileAlert/i18n.ts b/src/explorer/deleteFileAlert/i18n.ts index 718931cf..4e52d5f1 100644 --- a/src/explorer/deleteFileAlert/i18n.ts +++ b/src/explorer/deleteFileAlert/i18n.ts @@ -1,6 +1,14 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2022 The Pybricks Authors +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { Accept = 'action.accept', Cancel = 'action.cancel', diff --git a/src/explorer/duplicateFileDialog/DuplicateFileDialog.tsx b/src/explorer/duplicateFileDialog/DuplicateFileDialog.tsx index 5b3c1383..1413b448 100644 --- a/src/explorer/duplicateFileDialog/DuplicateFileDialog.tsx +++ b/src/explorer/duplicateFileDialog/DuplicateFileDialog.tsx @@ -2,7 +2,6 @@ // Copyright (c) 2022 The Pybricks Authors import { Button, Classes, Dialog } from '@blueprintjs/core'; -import { useI18n } from '@shopify/react-i18n'; import React, { useCallback, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import { useFileStorageMetadata } from '../../fileStorage/hooks'; @@ -13,11 +12,10 @@ import { import { useSelector } from '../../reducers'; import FileNameFormGroup from '../fileNameFormGroup/FileNameFormGroup'; import { duplicateFileDialogDidAccept, duplicateFileDialogDidCancel } from './actions'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; const DuplicateFileDialog: React.VFC = () => { - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); const dispatch = useDispatch(); const isOpen = useSelector((s) => s.explorer.duplicateFileDialog.isOpen); const oldName = useSelector((s) => s.explorer.duplicateFileDialog.fileName); diff --git a/src/explorer/duplicateFileDialog/i18n.ts b/src/explorer/duplicateFileDialog/i18n.ts index c380ebc0..2a535dd7 100644 --- a/src/explorer/duplicateFileDialog/i18n.ts +++ b/src/explorer/duplicateFileDialog/i18n.ts @@ -1,6 +1,14 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2022 The Pybricks Authors +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { Title = 'title', ActionAccept = 'action.accept', diff --git a/src/explorer/fileNameFormGroup/FileNameFormGroup.tsx b/src/explorer/fileNameFormGroup/FileNameFormGroup.tsx index 257ed5dc..71795007 100644 --- a/src/explorer/fileNameFormGroup/FileNameFormGroup.tsx +++ b/src/explorer/fileNameFormGroup/FileNameFormGroup.tsx @@ -2,16 +2,13 @@ // Copyright (c) 2022 The Pybricks Authors import { Classes, FormGroup, InputGroup, Intent, Tag } from '@blueprintjs/core'; -import { I18n, useI18n } from '@shopify/react-i18n'; import React from 'react'; import { FileNameValidationResult } from '../../pybricksMicropython/lib'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; type FileNameHelpTextProps = { /** The result of the file name validation. */ validation: FileNameValidationResult; - /** Translation context. */ - i18n: I18n; }; /** @@ -19,8 +16,9 @@ type FileNameHelpTextProps = { */ const FileNameHelpText: React.VoidFunctionComponent = ({ validation, - i18n, }) => { + const i18n = useI18n(); + switch (validation) { case FileNameValidationResult.IsOk: return <>{i18n.translate(I18nId.HelpTextIsOk)}; @@ -78,8 +76,7 @@ const FileNameFormGroup: React.VoidFunctionComponent = ( inputRef, onChange, }) => { - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); const fileNameIntent = validationResult === FileNameValidationResult.IsOk @@ -90,7 +87,7 @@ const FileNameFormGroup: React.VoidFunctionComponent = ( } + subLabel={} > { - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); const dispatch = useDispatch(); const isOpen = useSelector((s) => s.explorer.newFileWizard.isOpen); diff --git a/src/explorer/newFileWizard/i18n.ts b/src/explorer/newFileWizard/i18n.ts index e60cfcff..d15943b9 100644 --- a/src/explorer/newFileWizard/i18n.ts +++ b/src/explorer/newFileWizard/i18n.ts @@ -1,6 +1,14 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2022 The Pybricks Authors +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { Title = 'title', SmartHubLabel = 'smartHub.label', diff --git a/src/explorer/renameFileDialog/RenameFileDialog.tsx b/src/explorer/renameFileDialog/RenameFileDialog.tsx index 0e562cc9..89fecddc 100644 --- a/src/explorer/renameFileDialog/RenameFileDialog.tsx +++ b/src/explorer/renameFileDialog/RenameFileDialog.tsx @@ -2,7 +2,6 @@ // Copyright (c) 2022 The Pybricks Authors import { Button, Classes, Dialog } from '@blueprintjs/core'; -import { useI18n } from '@shopify/react-i18n'; import React, { useCallback, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import { useFileStorageMetadata } from '../../fileStorage/hooks'; @@ -13,11 +12,11 @@ import { import { useSelector } from '../../reducers'; import FileNameFormGroup from '../fileNameFormGroup/FileNameFormGroup'; import { renameFileDialogDidAccept, renameFileDialogDidCancel } from './actions'; +import { useI18n } from './i18n'; import { I18nId } from './i18n'; const RenameFileDialog: React.VFC = () => { - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); const dispatch = useDispatch(); const isOpen = useSelector((s) => s.explorer.renameFileDialog.isOpen); const oldName = useSelector((s) => s.explorer.renameFileDialog.fileName); diff --git a/src/explorer/renameFileDialog/i18n.ts b/src/explorer/renameFileDialog/i18n.ts index cbbf27c2..0836aec1 100644 --- a/src/explorer/renameFileDialog/i18n.ts +++ b/src/explorer/renameFileDialog/i18n.ts @@ -1,6 +1,14 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2022 The Pybricks Authors +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { Title = 'title', ActionRename = 'action.rename', diff --git a/src/licenses/LicenseDialog.tsx b/src/licenses/LicenseDialog.tsx index 4ced5aa8..1c8b55e2 100644 --- a/src/licenses/LicenseDialog.tsx +++ b/src/licenses/LicenseDialog.tsx @@ -11,7 +11,6 @@ import { NonIdealState, Spinner, } from '@blueprintjs/core'; -import { I18n, useI18n } from '@shopify/react-i18n'; import React, { useCallback, useMemo, useState } from 'react'; import { ControlledTreeEnvironment, @@ -23,7 +22,7 @@ import { import { useFetch } from 'usehooks-ts'; import { appName } from '../app/constants'; import { TreeItemData, renderers } from '../utils/tree-renderer'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; import './license.scss'; @@ -40,14 +39,12 @@ type LicenseList = ReadonlyArray; type LicenseListPanelProps = { /** Called when item is clicked. */ onItemClick(info?: LicenseInfo): void; - /** Translation context. */ - i18n: I18n; }; const LicenseListPanel: React.VoidFunctionComponent = ({ onItemClick, - i18n, }) => { + const i18n = useI18n(); const { data, error } = useFetch('static/oss-licenses.json'); const [focusedItem, setFocusedItem] = useState(); const [activeItem, setActiveItem] = useState(); @@ -124,12 +121,12 @@ const LicenseListPanel: React.VoidFunctionComponent = ({ type LicenseInfoPanelProps = { /** The license info to show or undefined if no license info is selected. */ licenseInfo: LicenseInfo | undefined; - /** Translation context. */ - i18n: I18n; }; const LicenseInfoPanel = React.forwardRef( - ({ licenseInfo, i18n }, ref) => { + ({ licenseInfo }, ref) => { + const i18n = useI18n(); + return (
{licenseInfo === undefined ? ( @@ -182,9 +179,7 @@ const LicenseDialog: React.VoidFunctionComponent = ({ }) => { const [licenseInfo, setLicenseInfo] = useState(undefined); const infoDiv = React.useRef(null); - - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); return ( = ({ infoDiv.current?.scrollTo(0, 0); setLicenseInfo(info); }} - i18n={i18n} - /> - +
diff --git a/src/licenses/i18n.ts b/src/licenses/i18n.ts index 01c55045..5b0d8f4b 100644 --- a/src/licenses/i18n.ts +++ b/src/licenses/i18n.ts @@ -3,6 +3,14 @@ // License dialog translation keys. +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { Title = 'title', Description = 'description', diff --git a/src/notifications/NotificationAction.tsx b/src/notifications/NotificationAction.tsx index f6f7893b..5fe760a9 100644 --- a/src/notifications/NotificationAction.tsx +++ b/src/notifications/NotificationAction.tsx @@ -3,9 +3,9 @@ // provides translation for notification text -import { Replacements, useI18n } from '@shopify/react-i18n'; +import { Replacements } from '@shopify/react-i18n'; import React from 'react'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; type NotificationActionProps = { messageId: I18nId; @@ -16,8 +16,7 @@ const NotificationAction: React.VoidFunctionComponent = messageId, replacements, }) => { - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); return <>{i18n.translate(messageId, replacements)}; }; diff --git a/src/notifications/NotificationMessage.tsx b/src/notifications/NotificationMessage.tsx index da5b8167..7fc4e6df 100644 --- a/src/notifications/NotificationMessage.tsx +++ b/src/notifications/NotificationMessage.tsx @@ -3,9 +3,9 @@ // provides translation for notification text -import { Replacements, useI18n } from '@shopify/react-i18n'; +import { Replacements } from '@shopify/react-i18n'; import React from 'react'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; type NotificationMessageProps = { messageId: I18nId; @@ -16,8 +16,7 @@ const NotificationMessage: React.VoidFunctionComponent messageId, replacements, }) => { - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); let message = i18n.translate(messageId, replacements) as | React.ReactElement diff --git a/src/notifications/i18n.ts b/src/notifications/i18n.ts index 7ac7e55b..d7dea7f7 100644 --- a/src/notifications/i18n.ts +++ b/src/notifications/i18n.ts @@ -3,6 +3,14 @@ // // Notification translation keys. +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { AppNoUpdateFound = 'app.noUpdateFound', BleUnexpectedError = 'ble.unexpectedError', diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index a7d631ab..ccd28ac9 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -12,7 +12,6 @@ import { Label, Switch, } from '@blueprintjs/core'; -import { useI18n } from '@shopify/react-i18n'; import React, { useState } from 'react'; import { useDispatch } from 'react-redux'; import { useTernaryDarkMode } from 'usehooks-ts'; @@ -36,7 +35,7 @@ import { useSettingHubName, useSettingIsShowDocsEnabled, } from './hooks'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; import './settings.scss'; const Settings: React.VoidFunctionComponent = () => { @@ -61,8 +60,7 @@ const Settings: React.VoidFunctionComponent = () => { const dispatch = useDispatch(); - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); return (
diff --git a/src/settings/i18n.ts b/src/settings/i18n.ts index fdb33991..a72e9dfc 100644 --- a/src/settings/i18n.ts +++ b/src/settings/i18n.ts @@ -3,6 +3,14 @@ // // Settings translation keys. +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { Title = 'title', AppearanceTitle = 'appearance.title', diff --git a/src/status-bar/StatusBar.tsx b/src/status-bar/StatusBar.tsx index 2a6dc878..224baeed 100644 --- a/src/status-bar/StatusBar.tsx +++ b/src/status-bar/StatusBar.tsx @@ -3,11 +3,10 @@ import { Button, Intent, ProgressBar } from '@blueprintjs/core'; import { Classes as Classes2, Popover2, Popover2Props } from '@blueprintjs/popover2'; -import { I18n, useI18n } from '@shopify/react-i18n'; import React from 'react'; import { BleConnectionState } from '../ble/reducers'; import { useSelector } from '../reducers'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; import './status-bar.scss'; @@ -16,12 +15,8 @@ const commonPopoverProps: Partial = { placement: 'top', }; -type HubInfoButtonProps = { - /** Translation context. */ - i18n: I18n; -}; - -const HubInfoButton: React.VoidFunctionComponent = ({ i18n }) => { +const HubInfoButton: React.VoidFunctionComponent = () => { + const i18n = useI18n(); const deviceName = useSelector((s) => s.ble.deviceName); const deviceType = useSelector((s) => s.ble.deviceType); const deviceFirmwareVersion = useSelector((s) => s.ble.deviceFirmwareVersion); @@ -65,14 +60,8 @@ const HubInfoButton: React.VoidFunctionComponent = ({ i18n } ); }; -type BatteryIndicatorProps = { - /** Translation context. */ - i18n: I18n; -}; - -const BatteryIndicator: React.VoidFunctionComponent = ({ - i18n, -}) => { +const BatteryIndicator: React.VoidFunctionComponent = () => { + const i18n = useI18n(); const charging = useSelector((s) => s.ble.deviceBatteryCharging); const lowBatteryWarning = useSelector((s) => s.ble.deviceLowBatteryWarning); @@ -106,17 +95,14 @@ const BatteryIndicator: React.VoidFunctionComponent = ({ }; const StatusBar: React.VFC = (_props) => { - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); - const connection = useSelector((s) => s.ble.connection); return (
{connection === BleConnectionState.Connected && ( <> - - + + )}
diff --git a/src/status-bar/i18n.ts b/src/status-bar/i18n.ts index f00d261a..cc20a639 100644 --- a/src/status-bar/i18n.ts +++ b/src/status-bar/i18n.ts @@ -3,6 +3,14 @@ // // Status bar translation keys. +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { BatteryTitle = 'battery.title', BatteryLow = 'battery.low', diff --git a/src/terminal/Terminal.tsx b/src/terminal/Terminal.tsx index 504f946a..e6def098 100644 --- a/src/terminal/Terminal.tsx +++ b/src/terminal/Terminal.tsx @@ -3,7 +3,6 @@ import { Menu, MenuDivider, MenuItem, ResizeSensor } from '@blueprintjs/core'; import { ContextMenu2, ContextMenu2ContentProps } from '@blueprintjs/popover2'; -import { useI18n } from '@shopify/react-i18n'; import React, { useContext, useEffect, useMemo, useRef } from 'react'; import { useDispatch } from 'react-redux'; import { useTernaryDarkMode } from 'usehooks-ts'; @@ -12,7 +11,7 @@ import { FitAddon } from 'xterm-addon-fit'; import { isMacOS } from '../utils/os'; import { TerminalContext } from './TerminalContext'; import { receiveData } from './actions'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; import 'xterm/css/xterm.css'; @@ -54,8 +53,7 @@ function createContextMenu( xterm: XTerm, ): (props: ContextMenu2ContentProps) => JSX.Element { const contextMenu = (_props: ContextMenu2ContentProps): JSX.Element => { - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); return ( diff --git a/src/terminal/i18n.ts b/src/terminal/i18n.ts index e8091901..842f7553 100644 --- a/src/terminal/i18n.ts +++ b/src/terminal/i18n.ts @@ -3,6 +3,14 @@ // // Terminal translation keys. +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { Copy = 'copy', Paste = 'paste', diff --git a/src/toolbar/buttons/bluetooth/BluetoothButton.tsx b/src/toolbar/buttons/bluetooth/BluetoothButton.tsx index f08051f8..05caaaae 100644 --- a/src/toolbar/buttons/bluetooth/BluetoothButton.tsx +++ b/src/toolbar/buttons/bluetooth/BluetoothButton.tsx @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2022 The Pybricks Authors -import { useI18n } from '@shopify/react-i18n'; import React from 'react'; import { useDispatch } from 'react-redux'; import { toggleBluetooth } from '../../../ble/actions'; @@ -11,7 +10,7 @@ import { useSelector } from '../../../reducers'; import ActionButton, { ActionButtonProps } from '../../ActionButton'; import connectedIcon from './connected.svg'; import disconnectedIcon from './disconnected.svg'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; type BluetoothButtonProps = Pick; @@ -23,8 +22,7 @@ const BluetoothButton: React.VoidFunctionComponent = ({ id bootloaderConnection === BootloaderConnectionState.Disconnected && bleConnection === BleConnectionState.Disconnected; - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); const dispatch = useDispatch(); return ( diff --git a/src/toolbar/buttons/bluetooth/i18n.ts b/src/toolbar/buttons/bluetooth/i18n.ts index 4b021874..a2cb4aeb 100644 --- a/src/toolbar/buttons/bluetooth/i18n.ts +++ b/src/toolbar/buttons/bluetooth/i18n.ts @@ -1,6 +1,14 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2022 The Pybricks Authors +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { Label = 'label', TooltipConnect = 'tooltip.connect', diff --git a/src/toolbar/buttons/flash/FlashButton.tsx b/src/toolbar/buttons/flash/FlashButton.tsx index 30e70d0c..29966d1c 100644 --- a/src/toolbar/buttons/flash/FlashButton.tsx +++ b/src/toolbar/buttons/flash/FlashButton.tsx @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2022 The Pybricks Authors -import { useI18n } from '@shopify/react-i18n'; import React from 'react'; import { useDispatch } from 'react-redux'; import { BleConnectionState } from '../../../ble/reducers'; @@ -14,7 +13,7 @@ import { useSettingHubName, } from '../../../settings/hooks'; import OpenFileButton, { OpenFileButtonProps } from '../../../toolbar/OpenFileButton'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; import icon from './icon.svg'; type FlashButtonProps = Pick; @@ -27,8 +26,7 @@ const FlashButton: React.VoidFunctionComponent = ({ id }) => { const [isSettingFlashCurrentProgramEnabled] = useSettingFlashCurrentProgram(); const { hubName } = useSettingHubName(); - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); const dispatch = useDispatch(); return ( diff --git a/src/toolbar/buttons/flash/i18n.ts b/src/toolbar/buttons/flash/i18n.ts index 4b6d51f7..72939203 100644 --- a/src/toolbar/buttons/flash/i18n.ts +++ b/src/toolbar/buttons/flash/i18n.ts @@ -1,6 +1,14 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2022 The Pybricks Authors +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { Label = 'label', TooltipAction = 'tooltip.action', diff --git a/src/toolbar/buttons/repl/ReplButton.tsx b/src/toolbar/buttons/repl/ReplButton.tsx index ad7e5cd5..2ad36d9f 100644 --- a/src/toolbar/buttons/repl/ReplButton.tsx +++ b/src/toolbar/buttons/repl/ReplButton.tsx @@ -1,21 +1,19 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2022 The Pybricks Authors -import { useI18n } from '@shopify/react-i18n'; import React, { useCallback } from 'react'; import { useDispatch } from 'react-redux'; import { repl } from '../../../hub/actions'; import { HubRuntimeState } from '../../../hub/reducers'; import { useSelector } from '../../../reducers'; import ActionButton, { ActionButtonProps } from '../../ActionButton'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; import icon from './icon.svg'; type ReplButtonProps = Pick; const ReplButton: React.VoidFunctionComponent = ({ id }) => { - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); const dispatch = useDispatch(); const enabled = useSelector((s) => s.hub.runtime === HubRuntimeState.Idle); diff --git a/src/toolbar/buttons/repl/i18n.ts b/src/toolbar/buttons/repl/i18n.ts index 8a962ff9..14dedb9a 100644 --- a/src/toolbar/buttons/repl/i18n.ts +++ b/src/toolbar/buttons/repl/i18n.ts @@ -1,6 +1,14 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2022 The Pybricks Authors +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { Label = 'label', Tooltip = 'tooltip', diff --git a/src/toolbar/buttons/run/RunButton.tsx b/src/toolbar/buttons/run/RunButton.tsx index cd8b142e..18d65a25 100644 --- a/src/toolbar/buttons/run/RunButton.tsx +++ b/src/toolbar/buttons/run/RunButton.tsx @@ -1,14 +1,13 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2022 The Pybricks Authors -import { useI18n } from '@shopify/react-i18n'; import React from 'react'; import { useDispatch } from 'react-redux'; import { downloadAndRun } from '../../../hub/actions'; import { HubRuntimeState } from '../../../hub/reducers'; import { useSelector } from '../../../reducers'; import ActionButton, { ActionButtonProps } from '../../ActionButton'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; import icon from './icon.svg'; type RunButtonProps = Pick; @@ -19,8 +18,7 @@ const RunButton: React.VoidFunctionComponent = ({ id }) => { const isEditorReady = useSelector((s) => s.editor.isReady); const keyboardShortcut = 'F5'; - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); const dispatch = useDispatch(); return ( diff --git a/src/toolbar/buttons/run/i18n.ts b/src/toolbar/buttons/run/i18n.ts index 4b6d51f7..72939203 100644 --- a/src/toolbar/buttons/run/i18n.ts +++ b/src/toolbar/buttons/run/i18n.ts @@ -1,6 +1,14 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2022 The Pybricks Authors +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { Label = 'label', TooltipAction = 'tooltip.action', diff --git a/src/toolbar/buttons/stop/StopButton.tsx b/src/toolbar/buttons/stop/StopButton.tsx index 5081edc6..82adcc32 100644 --- a/src/toolbar/buttons/stop/StopButton.tsx +++ b/src/toolbar/buttons/stop/StopButton.tsx @@ -1,14 +1,13 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2022 The Pybricks Authors -import { useI18n } from '@shopify/react-i18n'; import React from 'react'; import { useDispatch } from 'react-redux'; import { stop } from '../../../hub/actions'; import { HubRuntimeState } from '../../../hub/reducers'; import { useSelector } from '../../../reducers'; import ActionButton, { ActionButtonProps } from '../../ActionButton'; -import { I18nId } from './i18n'; +import { I18nId, useI18n } from './i18n'; import icon from './icon.svg'; type StopButtonProps = Pick; @@ -17,8 +16,7 @@ const StopButton: React.VoidFunctionComponent = ({ id }) => { const runtime = useSelector((s) => s.hub.runtime); const keyboardShortcut = 'F6'; - // istanbul ignore next: babel-loader rewrites this line - const [i18n] = useI18n(); + const i18n = useI18n(); const dispatch = useDispatch(); return ( diff --git a/src/toolbar/buttons/stop/i18n.ts b/src/toolbar/buttons/stop/i18n.ts index 8a962ff9..14dedb9a 100644 --- a/src/toolbar/buttons/stop/i18n.ts +++ b/src/toolbar/buttons/stop/i18n.ts @@ -1,6 +1,14 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2020-2022 The Pybricks Authors +import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n'; + +export function useI18n(): I18n { + // istanbul ignore next: babel-loader rewrites this line + const [i18n] = useShopifyI18n(); + return i18n; +} + export enum I18nId { Label = 'label', Tooltip = 'tooltip',