diff --git a/electron-builder.yml b/electron-builder.yml
index dd7af17a..be969905 100644
--- a/electron-builder.yml
+++ b/electron-builder.yml
@@ -7,7 +7,8 @@ afterSign: ./afterSignHook.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
diff --git a/lib/gui/app/app.ts b/lib/gui/app/app.ts
index 979b0879..27a39734 100644
--- a/lib/gui/app/app.ts
+++ b/lib/gui/app/app.ts
@@ -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 * as i18next from 'i18next';
window.addEventListener(
'unhandledrejection',
@@ -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) {
diff --git a/lib/gui/app/components/drive-selector/drive-selector.tsx b/lib/gui/app/components/drive-selector/drive-selector.tsx
index 4088271a..c10413dc 100644
--- a/lib/gui/app/components/drive-selector/drive-selector.tsx
+++ b/lib/gui/app/components/drive-selector/drive-selector.tsx
@@ -44,6 +44,7 @@ import {
import { SourceMetadata } from '../source-selector/source-selector';
import { middleEllipsis } from '../../utils/middle-ellipsis';
+import * as i18next from 'i18next';
interface UsbbootDrive extends sourceDestination.UsbbootDrive {
progress: number;
@@ -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);
@@ -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);
@@ -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 (
@@ -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 })}
}
titleDetails={{getDrives().length} found}
cancel={() => cancel(this.originalList)}
done={() => done(selectedList)}
- action={`Select (${selectedList.length})`}
+ action={i18next.t('drives.select', { select: selectedList.length })}
primaryButtonProps={{
primary: !showWarnings,
warning: showWarnings,
@@ -512,7 +513,11 @@ export class DriveSelector extends React.Component<
>
- Show {numberOfHiddenSystemDrives} hidden
+
+ {i18next.t('drives.showHidden', {
+ num: numberOfHiddenSystemDrives,
+ })}
+
)}
@@ -520,7 +525,7 @@ export class DriveSelector extends React.Component<
)}
{this.props.showWarnings && hasSystemDrives ? (
- Selecting your system drive is dangerous and will erase your drive!
+ {i18next.t('drives.systemDriveDanger')}
) : null}
@@ -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,
+ })
}
/>
)}
diff --git a/lib/gui/app/components/drive-status-warning-modal/drive-status-warning-modal.tsx b/lib/gui/app/components/drive-status-warning-modal/drive-status-warning-modal.tsx
index 4000917e..d5096b40 100644
--- a/lib/gui/app/components/drive-status-warning-modal/drive-status-warning-modal.tsx
+++ b/lib/gui/app/components/drive-status-warning-modal/drive-status-warning-modal.tsx
@@ -7,6 +7,7 @@ import { middleEllipsis } from '../../utils/middle-ellipsis';
import * as prettyBytes from 'pretty-bytes';
import { DriveWithWarnings } from '../../pages/main/Flash';
+import * as i18next from 'i18next';
const DriveStatusWarningModal = ({
done,
@@ -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 (
- WARNING!
+ {i18next.t('warning')}
{warningSubtitle}
diff --git a/lib/gui/app/components/flash-another/flash-another.tsx b/lib/gui/app/components/flash-another/flash-another.tsx
index 3b5741a3..c2246317 100644
--- a/lib/gui/app/components/flash-another/flash-another.tsx
+++ b/lib/gui/app/components/flash-another/flash-another.tsx
@@ -17,6 +17,7 @@
import * as React from 'react';
import { BaseButton } from '../../styled-components';
+import * as i18next from 'i18next';
export interface FlashAnotherProps {
onClick: () => void;
@@ -25,7 +26,7 @@ export interface FlashAnotherProps {
export const FlashAnother = (props: FlashAnotherProps) => {
return (
- Flash another
+ {i18next.t('flash.another')}
);
};
diff --git a/lib/gui/app/components/flash-results/flash-results.tsx b/lib/gui/app/components/flash-results/flash-results.tsx
index 05de2b3d..99e66937 100644
--- a/lib/gui/app/components/flash-results/flash-results.tsx
+++ b/lib/gui/app/components/flash-results/flash-results.tsx
@@ -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 * as i18next from 'i18next';
const ErrorsTable = styled((props) => {...props} />)`
&&& [data-display='table-head'],
@@ -88,15 +89,15 @@ function formattedErrors(errors: FlashError[]) {
const columns: Array> = [
{
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;
},
@@ -162,9 +163,11 @@ export function FlashResults({
{middleEllipsis(image, 24)}
- Flash {allFailed ? 'Failed' : 'Complete'}!
+ {allFailed
+ ? i18next.t('flash.flashFailed')
+ : i18next.t('flash.flashCompleted')}
- {skip ? Validation has been skipped : null}
+ {skip ? {i18next.t('flash.skip')} : null}
{results.devices.successful !== 0 ? (
@@ -188,7 +191,7 @@ export function FlashResults({
{progress.failed(errors.length)}
setShowErrorsInfo(true)}>
- more info
+ {i18next.t('flash.moreInfo')}
) : null}
@@ -199,12 +202,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', { speed: effectiveSpeed })}
)}
@@ -214,11 +214,11 @@ export function FlashResults({
titleElement={
- Failed targets
+ {i18next.t('failedTarget')}
}
- action="Retry failed targets"
+ action={i18next.t('failedRetry')}
cancel={() => setShowErrorsInfo(false)}
done={() => {
setShowErrorsInfo(false);
diff --git a/lib/gui/app/components/progress-button/progress-button.tsx b/lib/gui/app/components/progress-button/progress-button.tsx
index 43fe70e0..0986bee6 100644
--- a/lib/gui/app/components/progress-button/progress-button.tsx
+++ b/lib/gui/app/components/progress-button/progress-button.tsx
@@ -20,6 +20,7 @@ import { default as styled } from 'styled-components';
import { fromFlashState } from '../../modules/progress-status';
import { StepButton } from '../../styled-components';
+import * as i18next from 'i18next';
const FlashProgressBar = styled(ProgressBar)`
> div {
@@ -28,6 +29,7 @@ const FlashProgressBar = styled(ProgressBar)`
color: white !important;
text-shadow: none !important;
transition-duration: 0s;
+
> div {
transition-duration: 0s;
}
@@ -61,7 +63,7 @@ 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 (