Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

minor: make i18n support and Chinese translation #3790

Closed
wants to merge 10 commits into from
4 changes: 4 additions & 0 deletions .gitignore
Expand Up @@ -51,3 +51,7 @@ node_modules
# VSCode files

.vscode

# JetBrains WebStorm files

.idea
3 changes: 2 additions & 1 deletion electron-builder.yml
Expand Up @@ -8,7 +8,8 @@ afterPack: "./afterPack.js"
asar: false
files:
- generated
- lib/shared/catalina-sudo/sudo-askpass.osascript.js
- lib/shared/catalina-sudo/sudo-askpass.osascript-zh.js
- lib/shared/catalina-sudo/sudo-askpass.osascript-en.js
mac:
icon: assets/icon.icns
category: public.app-category.developer-tools
Expand Down
7 changes: 4 additions & 3 deletions lib/gui/app/app.ts
Expand Up @@ -38,6 +38,7 @@ import * as osDialog from './os/dialog';
import * as windowProgress from './os/window-progress';
import MainPage from './pages/main/MainPage';
import './css/main.css';
import i18next from 'i18next';

window.addEventListener(
'unhandledrejection',
Expand Down Expand Up @@ -313,9 +314,9 @@ window.addEventListener('beforeunload', async (event) => {

try {
const confirmed = await osDialog.showWarning({
confirmationLabel: 'Yes, quit',
rejectionLabel: 'Cancel',
title: 'Are you sure you want to close Etcher?',
confirmationLabel: i18next.t('yesExit'),
rejectionLabel: i18next.t('cancel'),
title: i18next.t('reallyExit'),
description: messages.warning.exitWhileFlashing(),
});
if (confirmed) {
Expand Down
27 changes: 17 additions & 10 deletions lib/gui/app/components/drive-selector/drive-selector.tsx
Expand Up @@ -44,6 +44,7 @@ import {

import { SourceMetadata } from '../source-selector/source-selector';
import { middleEllipsis } from '../../utils/middle-ellipsis';
import i18next from 'i18next';

interface UsbbootDrive extends sourceDestination.UsbbootDrive {
progress: number;
Expand Down Expand Up @@ -189,7 +190,7 @@ export class DriveSelector extends React.Component<
this.tableColumns = [
{
field: 'description',
label: 'Name',
label: i18next.t('drives.name'),
render: (description: string, drive: Drive) => {
if (isDrivelistDrive(drive)) {
const isLargeDrive = isDriveSizeLarge(drive);
Expand All @@ -215,7 +216,7 @@ export class DriveSelector extends React.Component<
{
field: 'description',
key: 'size',
label: 'Size',
label: i18next.t('drives.size'),
render: (_description: string, drive: Drive) => {
if (isDrivelistDrive(drive) && drive.size !== null) {
return prettyBytes(drive.size);
Expand All @@ -225,7 +226,7 @@ export class DriveSelector extends React.Component<
{
field: 'description',
key: 'link',
label: 'Location',
label: i18next.t('drives.location'),
render: (_description: string, drive: Drive) => {
return (
<Txt>
Expand Down Expand Up @@ -399,14 +400,14 @@ export class DriveSelector extends React.Component<
color="#5b82a7"
style={{ fontWeight: 600 }}
>
{drives.length} found
{i18next.t('drives.find', { length: drives.length })}
</Txt>
</Flex>
}
titleDetails={<Txt fontSize={11}>{getDrives().length} found</Txt>}
cancel={() => cancel(this.originalList)}
done={() => done(selectedList)}
action={`Select (${selectedList.length})`}
action={i18next.t('drives.select', { select: selectedList.length })}
primaryButtonProps={{
primary: !showWarnings,
warning: showWarnings,
Expand Down Expand Up @@ -512,15 +513,19 @@ export class DriveSelector extends React.Component<
>
<Flex alignItems="center">
<ChevronDownSvg height="1em" fill="currentColor" />
<Txt ml={8}>Show {numberOfHiddenSystemDrives} hidden</Txt>
<Txt ml={8}>
{i18next.t('drives.showHidden', {
num: numberOfHiddenSystemDrives,
})}
</Txt>
</Flex>
</Link>
)}
</>
)}
{this.props.showWarnings && hasSystemDrives ? (
<Alert className="system-drive-alert" style={{ width: '67%' }}>
Selecting your system drive is dangerous and will erase your drive!
{i18next.t('drives.systemDriveDanger')}
</Alert>
) : null}

Expand All @@ -540,13 +545,15 @@ export class DriveSelector extends React.Component<
this.setState({ missingDriversModal: {} });
}
}}
action="Yes, continue"
action={i18next.t('yesContinue')}
cancelButtonProps={{
children: 'Cancel',
children: i18next.t('cancel'),
}}
children={
missingDriversModal.drive.linkMessage ||
`Etcher will open ${missingDriversModal.drive.link} in your browser`
i18next.t('drives.openInBrowser', {
link: missingDriversModal.drive.link,
})
}
/>
)}
Expand Down
Expand Up @@ -7,6 +7,7 @@ import { middleEllipsis } from '../../utils/middle-ellipsis';

import * as prettyBytes from 'pretty-bytes';
import { DriveWithWarnings } from '../../pages/main/Flash';
import i18next from 'i18next';

const DriveStatusWarningModal = ({
done,
Expand All @@ -17,12 +18,12 @@ const DriveStatusWarningModal = ({
isSystem: boolean;
drivesWithWarnings: DriveWithWarnings[];
}) => {
let warningSubtitle = 'You are about to erase an unusually large drive';
let warningCta = 'Are you sure the selected drive is not a storage drive?';
let warningSubtitle = i18next.t('drives.largeDriveWarning');
let warningCta = i18next.t('drives.largeDriveWarningMsg');

if (isSystem) {
warningSubtitle = "You are about to erase your computer's drives";
warningCta = 'Are you sure you want to flash your system drive?';
warningSubtitle = i18next.t('drives.systemDriveWarning');
warningCta = i18next.t('drives.systemDriveWarningMsg');
}
return (
<Modal
Expand All @@ -33,9 +34,9 @@ const DriveStatusWarningModal = ({
cancelButtonProps={{
primary: false,
warning: true,
children: 'Change target',
children: i18next.t('drives.changeTarget'),
}}
action={"Yes, I'm sure"}
action={i18next.t('sure')}
primaryButtonProps={{
primary: false,
outline: true,
Expand All @@ -50,7 +51,7 @@ const DriveStatusWarningModal = ({
<Flex flexDirection="column">
<ExclamationTriangleSvg height="2em" fill="#fca321" />
<Txt fontSize="24px" color="#fca321">
WARNING!
{i18next.t('warning')}
</Txt>
</Flex>
<Txt fontSize="24px">{warningSubtitle}</Txt>
Expand Down
3 changes: 2 additions & 1 deletion lib/gui/app/components/flash-another/flash-another.tsx
Expand Up @@ -17,6 +17,7 @@
import * as React from 'react';

import { BaseButton } from '../../styled-components';
import i18next from 'i18next';

export interface FlashAnotherProps {
onClick: () => void;
Expand All @@ -25,7 +26,7 @@ export interface FlashAnotherProps {
export const FlashAnother = (props: FlashAnotherProps) => {
return (
<BaseButton primary onClick={props.onClick}>
Flash another
{i18next.t('flash.another')}
</BaseButton>
);
};
25 changes: 12 additions & 13 deletions lib/gui/app/components/flash-results/flash-results.tsx
Expand Up @@ -31,6 +31,7 @@ import { resetState } from '../../models/flash-state';
import * as selection from '../../models/selection-state';
import { middleEllipsis } from '../../utils/middle-ellipsis';
import { Modal, Table } from '../../styled-components';
import i18next from 'i18next';

const ErrorsTable = styled((props) => <Table<FlashError> {...props} />)`
&&& [data-display='table-head'],
Expand Down Expand Up @@ -88,15 +89,15 @@ function formattedErrors(errors: FlashError[]) {
const columns: Array<TableColumn<FlashError>> = [
{
field: 'description',
label: 'Target',
label: i18next.t('flash.target'),
},
{
field: 'device',
label: 'Location',
label: i18next.t('flash.location'),
},
{
field: 'message',
label: 'Error',
label: i18next.t('flash.error'),
render: (message: string, { code }: FlashError) => {
return message ?? code;
},
Expand Down Expand Up @@ -162,9 +163,10 @@ export function FlashResults({
<Txt>{middleEllipsis(image, 24)}</Txt>
</Flex>
<Txt fontSize={24} color="#fff" mb="17px">
Flash {allFailed ? 'Failed' : 'Complete'}!
{i18next.t('flash.flash')}{' '}
{allFailed ? i18next.t('failed') : i18next.t('completed')}!
RoderickQiu marked this conversation as resolved.
Show resolved Hide resolved
</Txt>
{skip ? <Txt color="#7e8085">Validation has been skipped</Txt> : null}
{skip ? <Txt color="#7e8085">{i18next.t('flash.skip')}</Txt> : null}
</Flex>
<Flex flexDirection="column" color="#7e8085">
{results.devices.successful !== 0 ? (
Expand All @@ -188,7 +190,7 @@ export function FlashResults({
{progress.failed(errors.length)}
</Txt>
<Link ml="10px" onClick={() => setShowErrorsInfo(true)}>
more info
{i18next.t('flash.moreInfo')}
</Link>
</Flex>
) : null}
Expand All @@ -199,12 +201,9 @@ export function FlashResults({
fontWeight: 500,
textAlign: 'center',
}}
tooltip={outdent({ newline: ' ' })`
The speed is calculated by dividing the image size by the flashing time.
Disk images with ext partitions flash faster as we are able to skip unused parts.
`}
tooltip={i18next.t('flash.speedTip')}
>
Effective speed: {effectiveSpeed} MB/s
{i18next.t('flash.speed')} {effectiveSpeed} MB/s
RoderickQiu marked this conversation as resolved.
Show resolved Hide resolved
</Txt>
)}
</Flex>
Expand All @@ -214,11 +213,11 @@ export function FlashResults({
titleElement={
<Flex alignItems="baseline" mb={18}>
<Txt fontSize={24} align="left">
Failed targets
{i18next.t('failedTarget')}
</Txt>
</Flex>
}
action="Retry failed targets"
action={i18next.t('failedRetry')}
cancel={() => setShowErrorsInfo(false)}
done={() => {
setShowErrorsInfo(false);
Expand Down
7 changes: 5 additions & 2 deletions lib/gui/app/components/progress-button/progress-button.tsx
Expand Up @@ -20,6 +20,7 @@ import { default as styled } from 'styled-components';

import { fromFlashState } from '../../modules/progress-status';
import { StepButton } from '../../styled-components';
import i18next from 'i18next';

const FlashProgressBar = styled(ProgressBar)`
> div {
Expand All @@ -28,6 +29,7 @@ const FlashProgressBar = styled(ProgressBar)`
color: white !important;
text-shadow: none !important;
transition-duration: 0s;

> div {
transition-duration: 0s;
}
Expand Down Expand Up @@ -61,14 +63,15 @@ const colors = {
} as const;

const CancelButton = styled(({ type, onClick, ...props }) => {
const status = type === 'verifying' ? 'Skip' : 'Cancel';
const status = type === 'verifying' ? i18next.t('skip') : i18next.t('cancel');
return (
<Button plain onClick={() => onClick(status)} {...props}>
{status}
</Button>
);
})`
font-weight: 600;

&&& {
width: auto;
height: auto;
Expand Down Expand Up @@ -126,7 +129,7 @@ export class ProgressButton extends React.PureComponent<ProgressButtonProps> {
marginTop: 30,
}}
>
Flash!
{i18next.t('flash.flashNow')}
</StepButton>
);
}
Expand Down
10 changes: 6 additions & 4 deletions lib/gui/app/components/settings/settings.tsx
Expand Up @@ -24,6 +24,7 @@ import * as settings from '../../models/settings';
import * as analytics from '../../modules/analytics';
import { open as openExternal } from '../../os/open-external/services/open-external';
import { Modal } from '../../styled-components';
import i18next from 'i18next';

interface Setting {
name: string;
Expand All @@ -34,13 +35,13 @@ async function getSettingsList(): Promise<Setting[]> {
const list: Setting[] = [
{
name: 'errorReporting',
label: 'Anonymously report errors and usage statistics to balena.io',
label: i18next.t('settings.errorReporting'),
},
];
if (['appimage', 'nsis', 'dmg'].includes(packageType)) {
list.push({
name: 'updatesEnabled',
label: 'Auto-updates enabled',
label: i18next.t('settings.autoUpdate'),
});
}
return list;
Expand All @@ -58,6 +59,7 @@ const InfoBox = (props: any) => (
<TextWithCopy code text={props.value} copy={props.value} />
</Box>
);

export function SettingsModal({ toggleModal }: SettingsModalProps) {
const [settingsList, setCurrentSettingsList] = React.useState<Setting[]>([]);
React.useEffect(() => {
Expand Down Expand Up @@ -92,7 +94,7 @@ export function SettingsModal({ toggleModal }: SettingsModalProps) {
<Modal
titleElement={
<Txt fontSize={24} mb={24}>
Settings
{i18next.t('settings.settings')}
</Txt>
}
done={() => toggleModal(false)}
Expand All @@ -113,7 +115,7 @@ export function SettingsModal({ toggleModal }: SettingsModalProps) {
})}
{UUID !== undefined && (
<Flex flexDirection="column">
<Txt fontSize={24}>System Information</Txt>
<Txt fontSize={24}>{i18next.t('settings.systemInformation')}</Txt>
<InfoBox label="UUID" value={UUID.substr(0, 7)} />
</Flex>
)}
Expand Down