i18n: use automatic type inference of translation keys

Finally figured out a way to make typescript strongly type translation
keys so we can avoid the tedium of creating enums of translation keys.
This commit is contained in:
David Lechner
2022-07-22 13:48:50 -05:00
parent 3e10096b6e
commit d6df128860
80 changed files with 402 additions and 875 deletions
+3 -3
View File
@@ -4,14 +4,14 @@
import { Intent } from '@blueprintjs/core';
import React from 'react';
import { CreateToast } from '../../i18nToaster';
import { I18nId, useI18n } from './i18n';
import { useI18n } from './i18n';
const BluetoothNotAvailable: React.VoidFunctionComponent = () => {
const i18n = useI18n();
return (
<>
<p>{i18n.translate(I18nId.BluetoothNotAvailableMessage)}</p>
<p>{i18n.translate(I18nId.BluetoothNotAvailableSuggestion)}</p>
<p>{i18n.translate('bluetoothNotAvailable.message')}</p>
<p>{i18n.translate('bluetoothNotAvailable.suggestion')}</p>
</>
);
};
+4 -4
View File
@@ -4,7 +4,7 @@
import { Intent } from '@blueprintjs/core';
import React from 'react';
import { CreateToast } from '../../i18nToaster';
import { I18nId, useI18n } from './i18n';
import { useI18n } from './i18n';
type MissingServiceProps = {
serviceName: string;
@@ -18,9 +18,9 @@ const MissingService: React.VoidFunctionComponent<MissingServiceProps> = ({
const i18n = useI18n();
return (
<>
<p>{i18n.translate(I18nId.MissingServiceMessage, { serviceName })}</p>
<p>{i18n.translate(I18nId.MissingServiceSuggestion1)}</p>
<p>{i18n.translate(I18nId.MissingServiceSuggestion2, { hubName })}</p>
<p>{i18n.translate('missingService.message', { serviceName })}</p>
<p>{i18n.translate('missingService.suggestion1')}</p>
<p>{i18n.translate('missingService.suggestion2', { hubName })}</p>
</>
);
};
+2 -2
View File
@@ -4,11 +4,11 @@
import { Intent } from '@blueprintjs/core';
import React from 'react';
import { CreateToast } from '../../i18nToaster';
import { I18nId, useI18n } from './i18n';
import { useI18n } from './i18n';
const NoGatt: React.VoidFunctionComponent = () => {
const i18n = useI18n();
return <p>{i18n.translate(I18nId.NoGattMessage)}</p>;
return <p>{i18n.translate('noGatt.message')}</p>;
};
export const noGatt: CreateToast = (onAction) => {
+7 -9
View File
@@ -7,7 +7,7 @@ import React from 'react';
import { appName, pybricksBluetoothTroubleshootingUrl } from '../../app/constants';
import { CreateToast } from '../../i18nToaster';
import ExternalLinkIcon from '../../utils/ExternalLinkIcon';
import { I18nId, useI18n } from './i18n';
import { useI18n } from './i18n';
type NoHubProps = {
onFlashFirmware: () => void;
@@ -18,28 +18,26 @@ const NoHub: React.VoidFunctionComponent<NoHubProps> = ({ onFlashFirmware }) =>
return (
<>
<p>{i18n.translate(I18nId.NoHubMessage)}</p>
<p>{i18n.translate('noHub.message')}</p>
<p>
{i18n.translate(I18nId.NoHubSuggestion1, {
{i18n.translate('noHub.suggestion1', {
appName,
buttonName: (
<strong>
{i18n.translate(I18nId.NoHubFlashFirmwareButton)}
</strong>
<strong>{i18n.translate('noHub.flashFirmwareButton')}</strong>
),
})}
</p>
<p>{i18n.translate(I18nId.NoHubSuggestion2)}</p>
<p>{i18n.translate('noHub.suggestion2')}</p>
<div className="pb-ble-alerts-buttons">
<Button icon="download" onClick={onFlashFirmware}>
{i18n.translate(I18nId.NoHubFlashFirmwareButton)}
{i18n.translate('noHub.flashFirmwareButton')}
</Button>
<AnchorButton
icon="help"
href={pybricksBluetoothTroubleshootingUrl}
target="_blank"
>
{i18n.translate(I18nId.NoHubTroubleshootButton)}
{i18n.translate('noHub.troubleshootButton')}
<ExternalLinkIcon />
</AnchorButton>
</div>
+4 -4
View File
@@ -5,19 +5,19 @@ import { Button, Intent } from '@blueprintjs/core';
import React from 'react';
import { CreateToast } from '../../i18nToaster';
import { isIOS, isLinux } from '../../utils/os';
import { I18nId, useI18n } from './i18n';
import { useI18n } from './i18n';
const NoWebBluetooth: React.VoidFunctionComponent = () => {
const i18n = useI18n();
return (
<>
<p>{i18n.translate(I18nId.NoWebBluetoothMessage)}</p>
<p>{i18n.translate('noWebBluetooth.message')}</p>
{!isLinux() && !isIOS() && (
<p>{i18n.translate(I18nId.NoWebBluetoothSuggestion)}</p>
<p>{i18n.translate('noWebBluetooth.suggestion')}</p>
)}
{isLinux() && (
<>
<p>{i18n.translate(I18nId.NoWebBluetoothLinux)}</p>
<p>{i18n.translate('noWebBluetooth.linux')}</p>
<p>
<code>
chrome://flags/#enable-experimental-web-platform-features
+3 -3
View File
@@ -5,7 +5,7 @@ import './index.scss';
import { Button, Intent } from '@blueprintjs/core';
import React from 'react';
import { CreateToast } from '../../i18nToaster';
import { I18nId, useI18n } from './i18n';
import { useI18n } from './i18n';
type OldFirmwareProps = {
onFlashFirmware: () => void;
@@ -18,10 +18,10 @@ const OldFirmware: React.VoidFunctionComponent<OldFirmwareProps> = ({
return (
<>
<p>{i18n.translate(I18nId.OldFirmwareMessage)}</p>
<p>{i18n.translate('oldFirmware.message')}</p>
<div className="pb-ble-alerts-buttons">
<Button icon="download" onClick={onFlashFirmware}>
{i18n.translate(I18nId.OldFirmwareFlashFirmwareLabel)}
{i18n.translate('oldFirmware.flashFirmware.label')}
</Button>
</div>
</>
-12
View File
@@ -1,12 +0,0 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
import { lookup } from '../../../test';
import { I18nId } from './i18n';
import en from './translations/en.json';
describe('Ensure .json file has matches for I18nId', () => {
test.each(Object.values(I18nId))('%s', (id) => {
expect(lookup(en, id)).toBeDefined();
});
});
+4 -21
View File
@@ -1,29 +1,12 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n';
import { useI18n as useShopifyI18n } from '@shopify/react-i18n';
import type { TypedI18n } from '../../i18n';
import type translations from './translations/en.json';
export function useI18n(): I18n {
export function useI18n(): TypedI18n<typeof translations> {
// istanbul ignore next: babel-loader rewrites this line
const [i18n] = useShopifyI18n();
return i18n;
}
export enum I18nId {
NoWebBluetoothMessage = 'noWebBluetooth.message',
NoWebBluetoothSuggestion = 'noWebBluetooth.suggestion',
NoWebBluetoothLinux = 'noWebBluetooth.linux',
BluetoothNotAvailableMessage = 'bluetoothNotAvailable.message',
BluetoothNotAvailableSuggestion = 'bluetoothNotAvailable.suggestion',
NoGattMessage = 'noGatt.message',
MissingServiceMessage = 'missingService.message',
MissingServiceSuggestion1 = 'missingService.suggestion1',
MissingServiceSuggestion2 = 'missingService.suggestion2',
NoHubMessage = 'noHub.message',
NoHubSuggestion1 = 'noHub.suggestion1',
NoHubSuggestion2 = 'noHub.suggestion2',
NoHubFlashFirmwareButton = 'noHub.flashFirmwareButton',
NoHubTroubleshootButton = 'noHub.troubleshootButton',
OldFirmwareMessage = 'oldFirmware.message',
OldFirmwareFlashFirmwareLabel = 'oldFirmware.flashFirmware.label',
}