From bea20d0495d49e15ddec860b10dc26852608928d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 2 Mar 2021 00:37:39 +0100 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=8C=90=20Add=20MVP=20for=20translatio?= =?UTF-8?q?n=20in=20the=20Supervisor=20panel=20(#8425)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bram Kragten --- build-scripts/gulp/gather-static.js | 5 + build-scripts/gulp/hassio.js | 6 + build-scripts/gulp/translations.js | 108 ++++++++----- build-scripts/gulp/webpack.js | 7 +- build-scripts/paths.js | 1 + hassio/src/addon-store/hassio-addon-store.ts | 9 +- .../addon-view/config/hassio-addon-config.ts | 10 ++ .../src/addon-view/hassio-addon-dashboard.ts | 7 +- hassio/src/dashboard/hassio-addons.ts | 18 ++- hassio/src/dashboard/hassio-dashboard.ts | 9 +- hassio/src/dashboard/hassio-update.ts | 89 ++++++++--- hassio/src/hassio-main.ts | 16 +- hassio/src/hassio-panel.ts | 16 +- hassio/src/hassio-tabs.ts | 8 +- hassio/src/snapshots/hassio-snapshots.ts | 9 +- hassio/src/supervisor-base-element.ts | 142 ++++++++++++------ hassio/src/system/hassio-system.ts | 9 +- src/common/config/version.ts | 7 +- src/data/hassio/addon.ts | 5 + src/data/supervisor/supervisor.ts | 3 + src/fake_data/provide_hass.ts | 2 +- src/layouts/hass-tabs-subpage.ts | 19 ++- src/mixins/lit-localize-lite-mixin.ts | 2 +- .../config/devices/ha-config-device-page.ts | 4 +- .../state/developer-tools-state.js | 11 +- .../lovelace/cards/hui-picture-entity-card.ts | 5 +- .../lovelace/cards/hui-picture-glance-card.ts | 5 +- src/state/translations-mixin.ts | 2 +- src/translations/en.json | 41 +++++ src/util/common-translation.ts | 56 +++++++ src/util/hass-translation.ts | 47 +----- 31 files changed, 470 insertions(+), 208 deletions(-) create mode 100644 src/util/common-translation.ts diff --git a/build-scripts/gulp/gather-static.js b/build-scripts/gulp/gather-static.js index ea5a29326f..452b4bf7c1 100644 --- a/build-scripts/gulp/gather-static.js +++ b/build-scripts/gulp/gather-static.js @@ -85,6 +85,11 @@ gulp.task("copy-translations-app", async () => { copyTranslations(staticDir); }); +gulp.task("copy-translations-supervisor", async () => { + const staticDir = paths.hassio_output_static; + copyTranslations(staticDir); +}); + gulp.task("copy-static-app", async () => { const staticDir = paths.app_output_static; // Basic static files diff --git a/build-scripts/gulp/hassio.js b/build-scripts/gulp/hassio.js index c055ba7685..49c5ecd6b6 100644 --- a/build-scripts/gulp/hassio.js +++ b/build-scripts/gulp/hassio.js @@ -10,6 +10,8 @@ require("./gen-icons-json.js"); require("./webpack.js"); require("./compress.js"); require("./rollup.js"); +require("./gather-static.js"); +require("./translations.js"); gulp.task( "develop-hassio", @@ -20,6 +22,8 @@ gulp.task( "clean-hassio", "gen-icons-json", "gen-index-hassio-dev", + "build-supervisor-translations", + "copy-translations-supervisor", env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio" ) ); @@ -32,6 +36,8 @@ gulp.task( }, "clean-hassio", "gen-icons-json", + "build-supervisor-translations", + "copy-translations-supervisor", env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio", "gen-index-hassio-prod", ...// Don't compress running tests diff --git a/build-scripts/gulp/translations.js b/build-scripts/gulp/translations.js index 609be106e7..e948a674db 100755 --- a/build-scripts/gulp/translations.js +++ b/build-scripts/gulp/translations.js @@ -266,6 +266,7 @@ gulp.task(taskName, function () { TRANSLATION_FRAGMENTS.forEach((fragment) => { delete data.ui.panel[fragment]; }); + delete data.supervisor; return data; }) ) @@ -342,6 +343,62 @@ gulp.task( } ); +gulp.task("build-translation-fragment-supervisor", function () { + return gulp + .src(fullDir + "/*.json") + .pipe(transform((data) => data.supervisor)) + .pipe(gulp.dest(workDir + "/supervisor")); +}); + +gulp.task("build-translation-flatten-supervisor", function () { + return gulp + .src(workDir + "/supervisor/*.json") + .pipe( + transform(function (data) { + // Polymer.AppLocalizeBehavior requires flattened json + return flatten(data); + }) + ) + .pipe(gulp.dest(outDir)); +}); + +gulp.task("build-translation-write-metadata", function writeMetadata() { + return gulp + .src( + [ + path.join(paths.translations_src, "translationMetadata.json"), + workDir + "/testMetadata.json", + workDir + "/translationFingerprints.json", + ], + { allowEmpty: true } + ) + .pipe(merge({})) + .pipe( + transform(function (data) { + const newData = {}; + Object.entries(data).forEach(([key, value]) => { + // Filter out translations without native name. + if (value.nativeName) { + newData[key] = value; + } else { + console.warn( + `Skipping language ${key}. Native name was not translated.` + ); + } + }); + return newData; + }) + ) + .pipe( + transform((data) => ({ + fragments: TRANSLATION_FRAGMENTS, + translations: data, + })) + ) + .pipe(rename("translationMetadata.json")) + .pipe(gulp.dest(workDir)); +}); + gulp.task( "build-translations", gulp.series( @@ -353,41 +410,20 @@ gulp.task( gulp.parallel(...splitTasks), "build-flattened-translations", "build-translation-fingerprints", - function writeMetadata() { - return gulp - .src( - [ - path.join(paths.translations_src, "translationMetadata.json"), - workDir + "/testMetadata.json", - workDir + "/translationFingerprints.json", - ], - { allowEmpty: true } - ) - .pipe(merge({})) - .pipe( - transform(function (data) { - const newData = {}; - Object.entries(data).forEach(([key, value]) => { - // Filter out translations without native name. - if (value.nativeName) { - newData[key] = value; - } else { - console.warn( - `Skipping language ${key}. Native name was not translated.` - ); - } - }); - return newData; - }) - ) - .pipe( - transform((data) => ({ - fragments: TRANSLATION_FRAGMENTS, - translations: data, - })) - ) - .pipe(rename("translationMetadata.json")) - .pipe(gulp.dest(workDir)); - } + "build-translation-write-metadata" + ) +); + +gulp.task( + "build-supervisor-translations", + gulp.series( + "clean-translations", + "ensure-translations-build-dir", + "build-master-translation", + "build-merged-translations", + "build-translation-fragment-supervisor", + "build-translation-flatten-supervisor", + "build-translation-fingerprints", + "build-translation-write-metadata" ) ); diff --git a/build-scripts/gulp/webpack.js b/build-scripts/gulp/webpack.js index c5790f0b69..2db685721f 100644 --- a/build-scripts/gulp/webpack.js +++ b/build-scripts/gulp/webpack.js @@ -137,7 +137,12 @@ gulp.task("webpack-watch-hassio", () => { isProdBuild: false, latestBuild: true, }) - ).watch({}, doneHandler()); + ).watch({ ignored: /build-translations/ }, doneHandler()); + + gulp.watch( + path.join(paths.translations_src, "en.json"), + gulp.series("build-supervisor-translations", "copy-translations-supervisor") + ); }); gulp.task("webpack-prod-hassio", () => diff --git a/build-scripts/paths.js b/build-scripts/paths.js index ae613b9b10..da9d8db190 100644 --- a/build-scripts/paths.js +++ b/build-scripts/paths.js @@ -34,6 +34,7 @@ module.exports = { hassio_dir: path.resolve(__dirname, "../hassio"), hassio_output_root: path.resolve(__dirname, "../hassio/build"), + hassio_output_static: path.resolve(__dirname, "../hassio/build/static"), hassio_output_latest: path.resolve( __dirname, "../hassio/build/frontend_latest" diff --git a/hassio/src/addon-store/hassio-addon-store.ts b/hassio/src/addon-store/hassio-addon-store.ts index 803ed6cbc4..2ccac48290 100644 --- a/hassio/src/addon-store/hassio-addon-store.ts +++ b/hassio/src/addon-store/hassio-addon-store.ts @@ -77,13 +77,16 @@ class HassioAddonStore extends LitElement { return html` - Add-on Store + + ${this.supervisor.localize("panel.store")} + { + return ( + this.addon.translations[this.hass.language]?.configuration?.[entry.name] + ?.name || + this.addon.translations.en?.configuration?.[entry.name].name || + entry.name + ); + }; + private _filteredShchema = memoizeOne( (options: Record, schema: HaFormSchema[]) => { return schema.filter((entry) => entry.name in options || entry.required); @@ -102,6 +111,7 @@ class HassioAddonConfig extends LitElement { ? html` ${this.addon.name} -

Add-ons

+

${this.supervisor.localize("dashboard.addons")}

${!this.supervisor.supervisor.addons?.length ? html`
- You don't have any add-ons installed yet. Head over to - to get started!
` @@ -58,10 +56,16 @@ class HassioAddons extends LitElement { ? mdiArrowUpBoldCircle : mdiPuzzle} .iconTitle=${addon.state !== "started" - ? "Add-on is stopped" + ? this.supervisor.localize( + "dashboard.addon_stopped" + ) : addon.update_available! - ? "New version available" - : "Add-on is running"} + ? this.supervisor.localize( + "dashboard.addon_new_version" + ) + : this.supervisor.localize( + "dashboard.addon_running" + )} .iconClass=${addon.update_available ? addon.state === "started" ? "update" diff --git a/hassio/src/dashboard/hassio-dashboard.ts b/hassio/src/dashboard/hassio-dashboard.ts index d2dac17237..1a4c373eda 100644 --- a/hassio/src/dashboard/hassio-dashboard.ts +++ b/hassio/src/dashboard/hassio-dashboard.ts @@ -29,13 +29,16 @@ class HassioDashboard extends LitElement { return html` - Dashboard + + ${this.supervisor.localize("panel.dashboard")} +
{ + return key === "os" ? version : `${key}-${version}`; +}; + @customElement("hassio-update") export class HassioUpdate extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -59,9 +68,12 @@ export class HassioUpdate extends LitElement { return html`

- ${updatesAvailable > 1 - ? "Updates Available 🎉" - : "Update Available 🎉"} + ${this.supervisor.localize( + "dashboard.update_available", + "count", + updatesAvailable + )} + 🎉

${this._renderUpdateCard( @@ -110,14 +122,30 @@ export class HassioUpdate extends LitElement {
-
${name} ${object.version_latest}
-
- You are currently running version ${object.version} -
+
${name}
+ + + ${this.supervisor.localize("common.version")} + + + ${computeVersion(key, object.version!)} + + + + + + ${this.supervisor.localize("common.newest_version")} + + + ${computeVersion(key, object.version_latest!)} + +
- Release notes + + ${this.supervisor.localize("common.release_notes")} + - Update + ${this.supervisor.localize("common.update")}
@@ -141,10 +169,20 @@ export class HassioUpdate extends LitElement { } item.progress = true; const confirmed = await showConfirmationDialog(this, { - title: `Update ${item.name}`, - text: `Are you sure you want to update ${item.name} to version ${item.version}?`, - confirmText: "update", - dismissText: "cancel", + title: this.supervisor.localize( + "confirm.update.title", + "name", + item.name + ), + text: this.supervisor.localize( + "confirm.update.text", + "name", + item.name, + "version", + computeVersion(item.key, item.version) + ), + confirmText: this.supervisor.localize("common.update"), + dismissText: this.supervisor.localize("common.cancel"), }); if (!confirmed) { @@ -152,7 +190,15 @@ export class HassioUpdate extends LitElement { return; } try { - await this.hass.callApi>("POST", item.apiPath); + if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) { + await supervisorApiWsRequest(this.hass.connection, { + method: "post", + endpoint: item.apiPath.replace("hassio", ""), + timeout: null, + }); + } else { + await this.hass.callApi>("POST", item.apiPath); + } fireEvent(this, "supervisor-colllection-refresh", { colllection: item.key, }); @@ -165,7 +211,7 @@ export class HassioUpdate extends LitElement { !ignoredStatusCodes.has(err.status_code) ) { showAlertDialog(this, { - title: "Update failed", + title: this.supervisor.localize("error.update_failed"), text: extractApiErrorMessage(err), }); } @@ -190,9 +236,6 @@ export class HassioUpdate extends LitElement { margin-bottom: 0.5em; color: var(--primary-text-color); } - .warning { - color: var(--secondary-text-color); - } .card-content { height: calc(100% - 47px); box-sizing: border-box; @@ -200,13 +243,13 @@ export class HassioUpdate extends LitElement { .card-actions { text-align: right; } - .errors { - color: var(--error-color); - padding: 16px; - } a { text-decoration: none; } + ha-settings-row { + padding: 0; + --paper-item-body-two-line-min-height: 32px; + } `, ]; } diff --git a/hassio/src/hassio-main.ts b/hassio/src/hassio-main.ts index 5283a8cb08..fd05060c94 100644 --- a/hassio/src/hassio-main.ts +++ b/hassio/src/hassio-main.ts @@ -3,7 +3,7 @@ import { atLeastVersion } from "../../src/common/config/version"; import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element"; import { fireEvent } from "../../src/common/dom/fire_event"; import { HassioPanelInfo } from "../../src/data/hassio/supervisor"; -import { supervisorCollection } from "../../src/data/supervisor/supervisor"; +import { Supervisor } from "../../src/data/supervisor/supervisor"; import { makeDialogManager } from "../../src/dialogs/make-dialog-manager"; import "../../src/layouts/hass-loading-screen"; import { HomeAssistant, Route } from "../../src/types"; @@ -14,6 +14,8 @@ import { SupervisorBaseElement } from "./supervisor-base-element"; export class HassioMain extends SupervisorBaseElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ attribute: false }) public supervisor!: Supervisor; + @property({ attribute: false }) public panel!: HassioPanelInfo; @property({ type: Boolean }) public narrow!: boolean; @@ -72,18 +74,6 @@ export class HassioMain extends SupervisorBaseElement { } protected render() { - if (!this.supervisor || !this.hass) { - return html``; - } - - if ( - Object.keys(supervisorCollection).some( - (colllection) => !this.supervisor![colllection] - ) - ) { - return html``; - } - return html` `; + } + + if ( + Object.keys(supervisorCollection).some( + (colllection) => !this.supervisor[colllection] + ) + ) { + return html``; + } return html` - Snapshots + + ${this.supervisor.localize("panel.snapshots")} + = { + localize: () => "", + }; @internalProperty() private _unsubs: Record = {}; @@ -49,6 +53,15 @@ export class SupervisorBaseElement extends urlSyncMixin( Collection > = {}; + @internalProperty() private _resources?: Record; + + @internalProperty() private _language = "en"; + + public connectedCallback(): void { + super.connectedCallback(); + this._initializeLocalize(); + } + public disconnectedCallback() { super.disconnectedCallback(); Object.keys(this._unsubs).forEach((unsub) => { @@ -56,15 +69,50 @@ export class SupervisorBaseElement extends urlSyncMixin( }); } + protected updated(changedProperties: PropertyValues) { + super.updated(changedProperties); + + if (changedProperties.has("_language")) { + if (changedProperties.get("_language") !== this._language) { + this._initializeLocalize(); + } + } + } + protected _updateSupervisor(obj: Partial): void { - this.supervisor = { ...this.supervisor!, ...obj }; + this.supervisor = { ...this.supervisor, ...obj }; } protected firstUpdated(changedProps: PropertyValues): void { super.firstUpdated(changedProps); + if (this._language !== this.hass.language) { + this._language = this.hass.language; + } + this._initializeLocalize(); this._initSupervisor(); } + private async _initializeLocalize() { + const { language, data } = await getTranslation( + null, + this._language, + "/api/hassio/app/static/translations" + ); + + this._resources = { + [language]: data, + }; + + this.supervisor = { + ...this.supervisor, + localize: await computeLocalize( + this.constructor.prototype, + this._language, + this._resources + ), + }; + } + private async _handleSupervisorStoreRefreshEvent(ev) { const colllection = ev.detail.colllection; if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) { @@ -104,52 +152,54 @@ export class SupervisorBaseElement extends urlSyncMixin( } }); - if (this.supervisor === undefined) { - Object.keys(this._collections).forEach((collection) => + Object.keys(this._collections).forEach((collection) => { + if ( + this.supervisor === undefined || + this.supervisor[collection] === undefined + ) { this._updateSupervisor({ [collection]: this._collections[collection].state, - }) - ); - } - return; + }); + } + }); + } else { + const [ + addon, + supervisor, + host, + core, + info, + os, + network, + resolution, + store, + ] = await Promise.all([ + fetchHassioAddonsInfo(this.hass), + fetchHassioSupervisorInfo(this.hass), + fetchHassioHostInfo(this.hass), + fetchHassioHomeAssistantInfo(this.hass), + fetchHassioInfo(this.hass), + fetchHassioHassOsInfo(this.hass), + fetchNetworkInfo(this.hass), + fetchHassioResolution(this.hass), + fetchSupervisorStore(this.hass), + ]); + + this.supervisor = { + addon, + supervisor, + host, + core, + info, + os, + network, + resolution, + store, + }; + + this.addEventListener("supervisor-update", (ev) => + this._updateSupervisor(ev.detail) + ); } - - const [ - addon, - supervisor, - host, - core, - info, - os, - network, - resolution, - store, - ] = await Promise.all([ - fetchHassioAddonsInfo(this.hass), - fetchHassioSupervisorInfo(this.hass), - fetchHassioHostInfo(this.hass), - fetchHassioHomeAssistantInfo(this.hass), - fetchHassioInfo(this.hass), - fetchHassioHassOsInfo(this.hass), - fetchNetworkInfo(this.hass), - fetchHassioResolution(this.hass), - fetchSupervisorStore(this.hass), - ]); - - this.supervisor = { - addon, - supervisor, - host, - core, - info, - os, - network, - resolution, - store, - }; - - this.addEventListener("supervisor-update", (ev) => - this._updateSupervisor(ev.detail) - ); } } diff --git a/hassio/src/system/hassio-system.ts b/hassio/src/system/hassio-system.ts index fa9999485e..5604ced62c 100644 --- a/hassio/src/system/hassio-system.ts +++ b/hassio/src/system/hassio-system.ts @@ -32,13 +32,16 @@ class HassioSystem extends LitElement { return html` - System + + ${this.supervisor.localize("panel.system")} +
major || - (Number(haMajor) === major && (patch === undefined - ? Number(haMinor) >= minor - : Number(haMinor) > minor)) || + (Number(haMajor) === major && + (patch === undefined + ? Number(haMinor) >= minor + : Number(haMinor) > minor)) || (patch !== undefined && Number(haMajor) === major && Number(haMinor) === minor && diff --git a/src/data/hassio/addon.ts b/src/data/hassio/addon.ts index 5ebc579547..99f035527c 100644 --- a/src/data/hassio/addon.ts +++ b/src/data/hassio/addon.ts @@ -16,6 +16,10 @@ export type AddonStartup = export type AddonState = "started" | "stopped" | null; export type AddonRepository = "core" | "local" | string; +interface AddonTranslations { + [key: string]: Record>>; +} + export interface HassioAddonInfo { advanced: boolean; available: boolean; @@ -82,6 +86,7 @@ export interface HassioAddonDetails extends HassioAddonInfo { slug: string; startup: AddonStartup; stdin: boolean; + translations: AddonTranslations; watchdog: null | boolean; webui: null | string; } diff --git a/src/data/supervisor/supervisor.ts b/src/data/supervisor/supervisor.ts index 4f1eff928a..1a38c4cbe2 100644 --- a/src/data/supervisor/supervisor.ts +++ b/src/data/supervisor/supervisor.ts @@ -1,5 +1,6 @@ import { Connection, getCollection } from "home-assistant-js-websocket"; import { Store } from "home-assistant-js-websocket/dist/store"; +import { LocalizeFunc } from "../../common/translations/localize"; import { HomeAssistant } from "../../types"; import { HassioAddonsInfo } from "../hassio/addon"; import { HassioHassOSInfo, HassioHostInfo } from "../hassio/host"; @@ -46,6 +47,7 @@ interface supervisorApiRequest { method?: "get" | "post" | "delete" | "put"; force_rest?: boolean; data?: any; + timeout?: number | null; } export interface SupervisorEvent { @@ -65,6 +67,7 @@ export interface Supervisor { os: HassioHassOSInfo; addon: HassioAddonsInfo; store: SupervisorStore; + localize: LocalizeFunc; } export const supervisorApiWsRequest = ( diff --git a/src/fake_data/provide_hass.ts b/src/fake_data/provide_hass.ts index ba4db6151e..dc1d29593a 100644 --- a/src/fake_data/provide_hass.ts +++ b/src/fake_data/provide_hass.ts @@ -7,7 +7,7 @@ import { computeLocalize } from "../common/translations/localize"; import { DEFAULT_PANEL } from "../data/panel"; import { translationMetadata } from "../resources/translations-metadata"; import { HomeAssistant } from "../types"; -import { getLocalLanguage, getTranslation } from "../util/hass-translation"; +import { getTranslation, getLocalLanguage } from "../util/hass-translation"; import { demoConfig } from "./demo_config"; import { demoPanels } from "./demo_panels"; import { demoServices } from "./demo_services"; diff --git a/src/layouts/hass-tabs-subpage.ts b/src/layouts/hass-tabs-subpage.ts index f7072e795c..01a32b45d7 100644 --- a/src/layouts/hass-tabs-subpage.ts +++ b/src/layouts/hass-tabs-subpage.ts @@ -16,6 +16,7 @@ import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../common/config/is_component_loaded"; import { restoreScroll } from "../common/decorators/restore-scroll"; import { navigate } from "../common/navigate"; +import { LocalizeFunc } from "../common/translations/localize"; import { computeRTL } from "../common/util/compute_rtl"; import "../components/ha-icon"; import "../components/ha-icon-button-arrow-prev"; @@ -40,7 +41,9 @@ export interface PageNavigation { class HassTabsSubpage extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property({ type: Boolean }) public hassio = false; + @property({ type: Boolean }) public supervisor = false; + + @property({ attribute: false }) public localizeFunc?: LocalizeFunc; @property({ type: String, attribute: "back-path" }) public backPath?: string; @@ -48,9 +51,9 @@ class HassTabsSubpage extends LitElement { @property({ type: Boolean, attribute: "main-page" }) public mainPage = false; - @property() public route!: Route; + @property({ attribute: false }) public route!: Route; - @property() public tabs!: PageNavigation[]; + @property({ attribute: false }) public tabs!: PageNavigation[]; @property({ type: Boolean, reflect: true }) public narrow = false; @@ -71,7 +74,8 @@ class HassTabsSubpage extends LitElement { showAdvanced: boolean | undefined, _components, _language, - _narrow + _narrow, + localizeFunc ) => { const shownTabs = tabs.filter( (page) => @@ -91,7 +95,7 @@ class HassTabsSubpage extends LitElement { .active=${page === activeTab} .narrow=${this.narrow} .name=${page.translationKey - ? this.hass.localize(page.translationKey) + ? localizeFunc(page.translationKey) : page.name} > ${page.iconPath @@ -130,7 +134,8 @@ class HassTabsSubpage extends LitElement { this.hass.userData?.showAdvanced, this.hass.config.components, this.hass.language, - this.narrow + this.narrow, + this.localizeFunc || this.hass.localize ); const showTabs = tabs.length > 1 || !this.narrow; return html` @@ -138,7 +143,7 @@ class HassTabsSubpage extends LitElement { ${this.mainPage ? html` diff --git a/src/mixins/lit-localize-lite-mixin.ts b/src/mixins/lit-localize-lite-mixin.ts index f1d2cf072c..cc723a36ac 100644 --- a/src/mixins/lit-localize-lite-mixin.ts +++ b/src/mixins/lit-localize-lite-mixin.ts @@ -1,7 +1,7 @@ import { LitElement, property, PropertyValues } from "lit-element"; import { computeLocalize, LocalizeFunc } from "../common/translations/localize"; import { Constructor, Resources } from "../types"; -import { getLocalLanguage, getTranslation } from "../util/hass-translation"; +import { getTranslation, getLocalLanguage } from "../util/hass-translation"; const empty = () => ""; diff --git a/src/panels/config/devices/ha-config-device-page.ts b/src/panels/config/devices/ha-config-device-page.ts index 6c882bb6a3..541ceabd16 100644 --- a/src/panels/config/devices/ha-config-device-page.ts +++ b/src/panels/config/devices/ha-config-device-page.ts @@ -161,8 +161,8 @@ export class HaConfigDevicePage extends LitElement { const batteryState = batteryEntity ? this.hass.states[batteryEntity.entity_id] : undefined; - const batteryIsBinary = batteryState - && computeStateDomain(batteryState) === "binary_sensor"; + const batteryIsBinary = + batteryState && computeStateDomain(batteryState) === "binary_sensor"; const batteryChargingState = batteryChargingEntity ? this.hass.states[batteryChargingEntity.entity_id] : undefined; diff --git a/src/panels/developer-tools/state/developer-tools-state.js b/src/panels/developer-tools/state/developer-tools-state.js index 5992f3e39f..a170068bc1 100644 --- a/src/panels/developer-tools/state/developer-tools-state.js +++ b/src/panels/developer-tools/state/developer-tools-state.js @@ -1,7 +1,7 @@ import "@material/mwc-button"; import { mdiInformationOutline, - mdiClipboardTextMultipleOutline + mdiClipboardTextMultipleOutline, } from "@mdi/js"; import "@polymer/paper-checkbox/paper-checkbox"; import "@polymer/paper-input/paper-input"; @@ -169,7 +169,10 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) { [[localize('ui.panel.developer-tools.tabs.states.state')]] [[localize('ui.panel.developer-tools.tabs.states.attributes')]] - + @@ -285,7 +288,9 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) { _showAttributes: { type: Boolean, - value: JSON.parse(localStorage.getItem("devToolsShowAttributes") || true), + value: JSON.parse( + localStorage.getItem("devToolsShowAttributes") || true + ), }, _entities: { diff --git a/src/panels/lovelace/cards/hui-picture-entity-card.ts b/src/panels/lovelace/cards/hui-picture-entity-card.ts index f6f34d0f25..49818e0b8a 100644 --- a/src/panels/lovelace/cards/hui-picture-entity-card.ts +++ b/src/panels/lovelace/cards/hui-picture-entity-card.ts @@ -199,7 +199,10 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard { left: 0; right: 0; bottom: 0; - background-color: var(--ha-picture-card-background-color, rgba(0, 0, 0, 0.3)); + background-color: var( + --ha-picture-card-background-color, + rgba(0, 0, 0, 0.3) + ); padding: 16px; font-size: 16px; line-height: 16px; diff --git a/src/panels/lovelace/cards/hui-picture-glance-card.ts b/src/panels/lovelace/cards/hui-picture-glance-card.ts index dd0999ffa0..3f9a9930bf 100644 --- a/src/panels/lovelace/cards/hui-picture-glance-card.ts +++ b/src/panels/lovelace/cards/hui-picture-glance-card.ts @@ -314,7 +314,10 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard { left: 0; right: 0; bottom: 0; - background-color: var(--ha-picture-card-background-color, rgba(0, 0, 0, 0.3)); + background-color: var( + --ha-picture-card-background-color, + rgba(0, 0, 0, 0.3) + ); padding: 4px 8px; font-size: 16px; line-height: 40px; diff --git a/src/state/translations-mixin.ts b/src/state/translations-mixin.ts index 8be391e3a3..cfb99bf845 100644 --- a/src/state/translations-mixin.ts +++ b/src/state/translations-mixin.ts @@ -12,8 +12,8 @@ import { translationMetadata } from "../resources/translations-metadata"; import { Constructor, HomeAssistant } from "../types"; import { storeState } from "../util/ha-pref-storage"; import { - getLocalLanguage, getTranslation, + getLocalLanguage, getUserLanguage, } from "../util/hass-translation"; import { HassBaseEl } from "./hass-base-mixin"; diff --git a/src/translations/en.json b/src/translations/en.json index d293cea316..1df9ec42d1 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3417,5 +3417,46 @@ } } } + }, + "supervisor": { + "addon": { + "panel": { + "configuration": "Configuration", + "documentation": "Documentation", + "info": "Info", + "log": "Log" + } + }, + "common": { + "cancel": "Cancel", + "newest_version": "Newest Version", + "release_notes": "Release notes", + "update": "Update", + "version": "Version" + }, + "confirm": { + "update": { + "title": "Update ${name}", + "text": "Are you sure you want to update {name} to version {version}?" + } + }, + "dashboard": { + "addon_new_version": "New version available", + "addon_running": "Add-on is running", + "addon_stopped": "Add-on is stopped", + "addons": "Add-ons", + "no_addons": "You don't have any add-ons installed yet. Head over to the add-on store to get started!", + "update_available": "{count, plural,\n one {Update}\n other {{count} Updates}\n} pending" + }, + "error": { + "unknown": "Unknown error", + "update_failed": "Update failed" + }, + "panel": { + "dashboard": "Dashboard", + "snapshots": "Snapshots", + "store": "Add-on Store", + "system": "System" + } } } diff --git a/src/util/common-translation.ts b/src/util/common-translation.ts new file mode 100644 index 0000000000..7f7e99b3fa --- /dev/null +++ b/src/util/common-translation.ts @@ -0,0 +1,56 @@ +import { translationMetadata } from "../resources/translations-metadata"; + +const DEFAULT_BASE_URL = "/static/translations"; + +// Store loaded translations in memory so translations are available immediately +// when DOM is created in Polymer. Even a cache lookup creates noticeable latency. +const translations = {}; + +async function fetchTranslation(fingerprint: string, base_url: string) { + const response = await fetch(`${base_url}/${fingerprint}`, { + credentials: "same-origin", + }); + if (!response.ok) { + throw new Error( + `Fail to fetch translation ${fingerprint}: HTTP response status is ${response.status}` + ); + } + return response.json(); +} + +export async function getTranslation( + fragment: string | null, + language: string, + base_url?: string +) { + const metadata = translationMetadata.translations[language]; + if (!metadata) { + if (language !== "en") { + return getTranslation(fragment, "en", base_url); + } + throw new Error("Language en is not found in metadata"); + } + + // nl-abcd.jon or logbook/nl-abcd.json + const fingerprint = `${fragment ? fragment + "/" : ""}${language}-${ + metadata.hash + }.json`; + + // Fetch translation from the server + if (!translations[fingerprint]) { + translations[fingerprint] = fetchTranslation( + fingerprint, + base_url || DEFAULT_BASE_URL + ) + .then((data) => ({ language, data })) + .catch((error) => { + delete translations[fingerprint]; + if (language !== "en") { + // Couldn't load selected translation. Try a fall back to en before failing. + return getTranslation(fragment, "en", base_url); + } + return Promise.reject(error); + }); + } + return translations[fingerprint]; +} diff --git a/src/util/hass-translation.ts b/src/util/hass-translation.ts index 67aa62b0c4..e0be32bd0a 100644 --- a/src/util/hass-translation.ts +++ b/src/util/hass-translation.ts @@ -1,6 +1,7 @@ import { fetchTranslationPreferences } from "../data/translation"; import { translationMetadata } from "../resources/translations-metadata"; import { HomeAssistant } from "../types"; +import { getTranslation as commonGetTranslation } from "./common-translation"; const STORAGE = window.localStorage || {}; @@ -93,55 +94,13 @@ export function getLocalLanguage() { return "en"; } -// Store loaded translations in memory so translations are available immediately -// when DOM is created in Polymer. Even a cache lookup creates noticeable latency. -const translations = {}; - -async function fetchTranslation(fingerprint) { - const response = await fetch(`/static/translations/${fingerprint}`, { - credentials: "same-origin", - }); - if (!response.ok) { - throw new Error( - `Fail to fetch translation ${fingerprint}: HTTP response status is ${response.status}` - ); - } - return response.json(); -} - export async function getTranslation( fragment: string | null, language: string ) { - const metadata = translationMetadata.translations[language]; - if (!metadata) { - if (language !== "en") { - return getTranslation(fragment, "en"); - } - throw new Error("Language en is not found in metadata"); - } - - // nl-abcd.jon or logbook/nl-abcd.json - const fingerprint = `${fragment ? fragment + "/" : ""}${language}-${ - metadata.hash - }.json`; - - // Fetch translation from the server - if (!translations[fingerprint]) { - translations[fingerprint] = fetchTranslation(fingerprint) - .then((data) => ({ language, data })) - .catch((error) => { - delete translations[fingerprint]; - if (language !== "en") { - // Couldn't load selected translation. Try a fall back to en before failing. - return getTranslation(fragment, "en"); - } - return Promise.reject(error); - }); - } - return translations[fingerprint]; + return commonGetTranslation(fragment, language); } // Load selected translation into memory immediately so it is ready when Polymer // initializes. -getTranslation(null, getLocalLanguage()); +commonGetTranslation(null, getLocalLanguage()); From 08f1ce2d548ea51c76b3746deecedcfee62ac664 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 2 Mar 2021 01:09:30 +0000 Subject: [PATCH 2/5] Translation update --- translations/frontend/en.json | 51 +++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/translations/frontend/en.json b/translations/frontend/en.json index aa5dc5d1cc..55d05fe89c 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -102,6 +102,47 @@ "unknown": "Unknown" } }, + "supervisor": { + "addon": { + "panel": { + "configuration": "Configuration", + "documentation": "Documentation", + "info": "Info", + "log": "Log" + } + }, + "common": { + "cancel": "Cancel", + "newest_version": "Newest Version", + "release_notes": "Release notes", + "update": "Update", + "version": "Version" + }, + "confirm": { + "update": { + "text": "Are you sure you want to update {name} to version {version}?", + "title": "Update ${name}" + } + }, + "dashboard": { + "addon_new_version": "New version available", + "addon_running": "Add-on is running", + "addon_stopped": "Add-on is stopped", + "addons": "Add-ons", + "no_addons": "You don't have any add-ons installed yet. Head over to the add-on store to get started!", + "update_available": "{count, plural,\n one {Update}\n other {{count} Updates}\n} pending" + }, + "error": { + "unknown": "Unknown error", + "update_failed": "Update failed" + }, + "panel": { + "dashboard": "Dashboard", + "snapshots": "Snapshots", + "store": "Add-on Store", + "system": "System" + } + }, "ui": { "auth_store": { "ask": "Do you want to stay logged in?", @@ -377,6 +418,8 @@ "changed_to_state": "changed to {state}", "cleared_device_class": "cleared (no {device_class} detected)", "detected_device_class": "detected {device_class}", + "is_closing": "is closing", + "is_opening": "is opening", "rose": "rose", "set": "set", "turned_off": "turned off", @@ -632,8 +675,8 @@ "remove_intro": "If the entity is no longer in use, you can clean it up by removing it." }, "script": { - "last_action": "Last Action", - "last_triggered": "Last Triggered" + "last_action": "Last action", + "last_triggered": "Last triggered" }, "settings": "Entity settings", "sun": { @@ -756,6 +799,7 @@ "clusters": "Manage Clusters", "reconfigure": "Reconfigure Device", "remove": "Remove Device", + "view_in_visualization": "View in Visualization", "zigbee_information": "Zigbee device signature" }, "confirmations": { @@ -2333,6 +2377,7 @@ "caption": "Visualization", "header": "Network Visualization", "highlight_label": "Highlight Devices", + "refresh_topology": "Refresh Topology", "zoom_label": "Zoom To Device" } }, @@ -3010,6 +3055,8 @@ "error_remove": "Unable to remove configuration: {error}", "error_save_yaml": "Unable to save YAML: {error}", "header": "Edit Configuration", + "lovelace_changed": "The Lovelace config was updated, do you want to load the updated config in the editor and lose your current changes?", + "reload": "Reload", "resources_moved": "Resources should no longer be added to the Lovelace configuration but can be added in the Lovelace config panel.", "save": "Save", "saved": "Saved", From 25a0be7672b6b588835fbf528bf1c5d0edadef30 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 2 Mar 2021 10:05:16 +0100 Subject: [PATCH 3/5] Bump codemirror view (#8512) Fixes search bug in Chrome --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 60759b2fbc..f2a742e304 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2026,9 +2026,9 @@ integrity sha512-KL+cM+uJPW5skyuTRoW43lOaSQq3YDNEPx5z0V/9Wsz9R9dK4kVP5NIRMUFgl9MUCQ9UxIotvgPDpz65j9wjuA== "@codemirror/view@^0.17.0": - version "0.17.11" - resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-0.17.11.tgz#d9fad5aa405769f97621bb77fab7abc2edb55f2d" - integrity sha512-pl5fOiBLifExuqAuqhv/yOZvDODQrO26HEtljv4DJqx2Em5kKjzWVHhQymq0dh+nPY+qA4vZCGcT5r/ySNR52Q== + version "0.17.12" + resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-0.17.12.tgz#3720323d32ad359372621f0b16f8e25a3d17a933" + integrity sha512-sHA57N2yfFQTgoOfF5OneHPAltupcSH4ps4cD3fj91F2EhWLqfzryImom8RxhJrwiE+3Kv3MiRtOXPthHIbswg== dependencies: "@codemirror/rangeset" "^0.17.0" "@codemirror/state" "^0.17.0" From 03b8c1348cee22f94f4af1dd7a02d13745ed2110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 2 Mar 2021 14:46:30 +0100 Subject: [PATCH 4/5] Use localize in the Supervisor panel (#8515) --- .../addon-store/hassio-addon-repository.ts | 19 +- hassio/src/addon-store/hassio-addon-store.ts | 15 +- .../addon-view/config/hassio-addon-audio.ts | 27 +- .../config/hassio-addon-config-tab.ts | 8 +- .../addon-view/config/hassio-addon-config.ts | 57 ++- .../addon-view/config/hassio-addon-network.ts | 64 ++- .../hassio-addon-documentation-tab.ts | 12 +- .../src/addon-view/hassio-addon-dashboard.ts | 4 - .../src/addon-view/info/hassio-addon-info.ts | 367 ++++++++++-------- .../addon-view/log/hassio-addon-log-tab.ts | 4 + .../src/addon-view/log/hassio-addon-logs.ts | 13 +- hassio/src/components/hassio-card-content.ts | 2 +- hassio/src/components/supervisor-metric.ts | 2 +- hassio/src/dashboard/hassio-update.ts | 4 +- .../dialogs/network/dialog-hassio-network.ts | 75 ++-- .../dialogs/network/show-dialog-network.ts | 4 +- .../registries/dialog-hassio-registries.ts | 48 ++- .../registries/show-dialog-registries.ts | 12 +- .../dialog-hassio-repositories.ts | 30 +- .../repositories/show-dialog-repositories.ts | 4 +- hassio/src/dialogs/suggestAddonRestart.ts | 16 +- hassio/src/hassio-my-redirect.ts | 19 +- hassio/src/snapshots/hassio-snapshots.ts | 73 ++-- hassio/src/system/hassio-core-info.ts | 42 +- hassio/src/system/hassio-host-info.ts | 111 +++--- hassio/src/system/hassio-supervisor-info.ts | 263 +++++++------ hassio/src/system/hassio-supervisor-log.ts | 28 +- hassio/src/system/hassio-system.ts | 5 +- src/translations/en.json | 361 ++++++++++++++++- 29 files changed, 1169 insertions(+), 520 deletions(-) diff --git a/hassio/src/addon-store/hassio-addon-repository.ts b/hassio/src/addon-store/hassio-addon-repository.ts index 67c4c50849..746ee5cd8d 100644 --- a/hassio/src/addon-store/hassio-addon-repository.ts +++ b/hassio/src/addon-store/hassio-addon-repository.ts @@ -15,6 +15,7 @@ import { HassioAddonInfo, HassioAddonRepository, } from "../../../src/data/hassio/addon"; +import { Supervisor } from "../../../src/data/supervisor/supervisor"; import { HomeAssistant } from "../../../src/types"; import "../components/hassio-card-content"; import { filterAndSort } from "../components/hassio-filter-addons"; @@ -23,6 +24,8 @@ import { hassioStyle } from "../resources/hassio-style"; class HassioAddonRepositoryEl extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ attribute: false }) public supervisor!: Supervisor; + @property({ attribute: false }) public repo!: HassioAddonRepository; @property({ attribute: false }) public addons!: HassioAddonInfo[]; @@ -54,7 +57,11 @@ class HassioAddonRepositoryEl extends LitElement { return html`

- No results found in "${repo.name}." + ${this.supervisor.localize( + "store.no_results_found", + "repository", + repo.name + )}

`; @@ -83,11 +90,13 @@ class HassioAddonRepositoryEl extends LitElement { : mdiPuzzle} .iconTitle=${addon.installed ? addon.update_available - ? "New version available" - : "Add-on is installed" + ? this.supervisor.localize( + "common.new_version_available" + ) + : this.supervisor.localize("addon.installed") : addon.available - ? "Add-on is not installed" - : "Add-on is not available on your system"} + ? this.supervisor.localize("addon.not_installed") + : this.supervisor.localize("addon.not_available")} .iconClass=${addon.installed ? addon.update_available ? "update" diff --git a/hassio/src/addon-store/hassio-addon-store.ts b/hassio/src/addon-store/hassio-addon-store.ts index 2ccac48290..80e06f36fd 100644 --- a/hassio/src/addon-store/hassio-addon-store.ts +++ b/hassio/src/addon-store/hassio-addon-store.ts @@ -96,15 +96,15 @@ class HassioAddonStore extends LitElement { - Repositories + ${this.supervisor.localize("store.repositories")} - Reload + ${this.supervisor.localize("common.reload")} ${this.hass.userData?.showAdvanced && atLeastVersion(this.hass.config.version, 0, 117) ? html` - Registries + ${this.supervisor.localize("store.registries")} ` : ""} @@ -125,11 +125,9 @@ class HassioAddonStore extends LitElement { ${!this.hass.userData?.showAdvanced ? html` ` : ""} @@ -161,6 +159,7 @@ class HassioAddonStore extends LitElement { .repo=${repo} .addons=${filteredAddons} .filter=${filter!} + .supervisor=${this.supervisor} > ` : html``; @@ -190,13 +189,13 @@ class HassioAddonStore extends LitElement { private async _manageRepositories() { showRepositoriesDialog(this, { - repos: this.supervisor.addon.repositories, + supervisor: this.supervisor, loadData: () => this._loadData(), }); } private async _manageRegistries() { - showRegistriesDialog(this); + showRegistriesDialog(this, { supervisor: this.supervisor }); } private async _loadData() { diff --git a/hassio/src/addon-view/config/hassio-addon-audio.ts b/hassio/src/addon-view/config/hassio-addon-audio.ts index e3349b0144..de682a9520 100644 --- a/hassio/src/addon-view/config/hassio-addon-audio.ts +++ b/hassio/src/addon-view/config/hassio-addon-audio.ts @@ -25,6 +25,7 @@ import { fetchHassioHardwareAudio, HassioHardwareAudioDevice, } from "../../../../src/data/hassio/hardware"; +import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { haStyle } from "../../../../src/resources/styles"; import { HomeAssistant } from "../../../../src/types"; import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart"; @@ -34,6 +35,8 @@ import { hassioStyle } from "../../resources/hassio-style"; class HassioAddonAudio extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ attribute: false }) public supervisor!: Supervisor; + @property({ attribute: false }) public addon!: HassioAddonDetails; @internalProperty() private _error?: string; @@ -48,12 +51,16 @@ class HassioAddonAudio extends LitElement { protected render(): TemplateResult { return html` - +
${this._error ? html`
${this._error}
` : ""} { return html` - ${item.name} + + ${item.name} + `; })}
- Save + ${this.supervisor.localize("common.save")}
@@ -152,7 +161,7 @@ class HassioAddonAudio extends LitElement { const noDevice: HassioHardwareAudioDevice = { device: "default", - name: "Default", + name: this.supervisor.localize("addon.configuration.audio.default"), }; try { @@ -189,7 +198,7 @@ class HassioAddonAudio extends LitElement { try { await setHassioAddonOption(this.hass, this.addon.slug, data); if (this.addon?.state === "started") { - await suggestAddonRestart(this, this.hass, this.addon); + await suggestAddonRestart(this, this.hass, this.supervisor, this.addon); } } catch { this._error = "Failed to set addon audio device"; diff --git a/hassio/src/addon-view/config/hassio-addon-config-tab.ts b/hassio/src/addon-view/config/hassio-addon-config-tab.ts index d59b150e18..b9a5907075 100644 --- a/hassio/src/addon-view/config/hassio-addon-config-tab.ts +++ b/hassio/src/addon-view/config/hassio-addon-config-tab.ts @@ -9,6 +9,7 @@ import { } from "lit-element"; import "../../../../src/components/ha-circular-progress"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; +import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { haStyle } from "../../../../src/resources/styles"; import { HomeAssistant } from "../../../../src/types"; import { hassioStyle } from "../../resources/hassio-style"; @@ -20,6 +21,8 @@ import "./hassio-addon-network"; class HassioAddonConfigDashboard extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ attribute: false }) public supervisor!: Supervisor; + @property({ attribute: false }) public addon?: HassioAddonDetails; protected render(): TemplateResult { @@ -39,6 +42,7 @@ class HassioAddonConfigDashboard extends LitElement { ` : ""} @@ -47,6 +51,7 @@ class HassioAddonConfigDashboard extends LitElement { ` : ""} @@ -55,11 +60,12 @@ class HassioAddonConfigDashboard extends LitElement { ` : ""} ` - : "This add-on does not expose configuration for you to mess with.... 👋"} + : this.supervisor.localize("addon.configuration.no_configuration")}
`; } diff --git a/hassio/src/addon-view/config/hassio-addon-config.ts b/hassio/src/addon-view/config/hassio-addon-config.ts index c81f417e26..2a5c2bfcfe 100644 --- a/hassio/src/addon-view/config/hassio-addon-config.ts +++ b/hassio/src/addon-view/config/hassio-addon-config.ts @@ -32,6 +32,7 @@ import { setHassioAddonOption, } from "../../../../src/data/hassio/addon"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; +import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box"; import { haStyle } from "../../../../src/resources/styles"; import type { HomeAssistant } from "../../../../src/types"; @@ -46,6 +47,8 @@ class HassioAddonConfig extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ attribute: false }) public supervisor!: Supervisor; + @property({ type: Boolean }) private _configHasChanged = false; @property({ type: Boolean }) private _valid = true; @@ -90,17 +93,25 @@ class HassioAddonConfig extends LitElement {

${this.addon.name}

-

Configuration

+

+ ${this.supervisor.localize("addon.configuration.options.header")} +

- ${this._yamlMode ? "Edit in UI" : "Edit in YAML"} + ${this._yamlMode + ? this.supervisor.localize( + "addon.configuration.options.edit_in_ui" + ) + : this.supervisor.localize( + "addon.configuration.options.edit_in_yaml" + )} - Reset to defaults + ${this.supervisor.localize("common.reset_defaults")}
@@ -127,12 +138,20 @@ class HassioAddonConfig extends LitElement { (this._canShowSchema && this.addon.schema) || this._valid ? "" - : html`
Invalid YAML
`} + : html` +
+ ${this.supervisor.localize( + "addon.configuration.options.invalid_yaml" + )} +
+ `}
${hasHiddenOptions ? html` - Save + Save ${this.supervisor.localize("common.save")}
@@ -211,10 +230,10 @@ class HassioAddonConfig extends LitElement { button.progress = true; const confirmed = await showConfirmationDialog(this, { - title: this.addon.name, - text: "Are you sure you want to reset all your options?", - confirmText: "reset options", - dismissText: "no", + title: this.supervisor.localize("confirm.reset_options.title"), + text: this.supervisor.localize("confirm.reset_options.text"), + confirmText: this.supervisor.localize("common.reset_options"), + dismissText: this.supervisor.localize("common.cancel"), }); if (!confirmed) { @@ -236,9 +255,11 @@ class HassioAddonConfig extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); } catch (err) { - this._error = `Failed to reset addon configuration, ${extractApiErrorMessage( - err - )}`; + this._error = this.supervisor.localize( + "addon.common.update_available", + "error", + extractApiErrorMessage(err) + ); } button.progress = false; } @@ -262,12 +283,14 @@ class HassioAddonConfig extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); if (this.addon?.state === "started") { - await suggestAddonRestart(this, this.hass, this.addon); + await suggestAddonRestart(this, this.hass, this.supervisor, this.addon); } } catch (err) { - this._error = `Failed to save addon configuration, ${extractApiErrorMessage( - err - )}`; + this._error = this.supervisor.localize( + "addon.configuration.options.failed_to_save", + "error", + extractApiErrorMessage(err) + ); } button.progress = false; } diff --git a/hassio/src/addon-view/config/hassio-addon-network.ts b/hassio/src/addon-view/config/hassio-addon-network.ts index 99f871be99..57eeacd187 100644 --- a/hassio/src/addon-view/config/hassio-addon-network.ts +++ b/hassio/src/addon-view/config/hassio-addon-network.ts @@ -19,6 +19,7 @@ import { setHassioAddonOption, } from "../../../../src/data/hassio/addon"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; +import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { haStyle } from "../../../../src/resources/styles"; import { HomeAssistant } from "../../../../src/types"; import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart"; @@ -38,6 +39,8 @@ interface NetworkItemInput extends PaperInputElement { class HassioAddonNetwork extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ attribute: false }) public supervisor!: Supervisor; + @property({ attribute: false }) public addon!: HassioAddonDetails; @internalProperty() private _error?: string; @@ -55,16 +58,30 @@ class HassioAddonNetwork extends LitElement { } return html` - +
${this._error ? html`
${this._error}
` : ""} - - - + + + ${this._config!.map((item) => { return html` @@ -73,13 +90,15 @@ class HassioAddonNetwork extends LitElement { - + `; })} @@ -88,10 +107,10 @@ class HassioAddonNetwork extends LitElement {
- Reset to defaults + ${this.supervisor.localize("common.reset_defaults")} - Save + ${this.supervisor.localize("common.save")}
@@ -105,6 +124,15 @@ class HassioAddonNetwork extends LitElement { } } + private _computeDescription = (item: NetworkItem): string => { + return ( + this.addon.translations[this.hass.language]?.network?.[item.container] + ?.description || + this.addon.translations.en?.network?.[item.container]?.description || + item.description + ); + }; + private _setNetworkConfig(): void { const network = this.addon.network || {}; const description = this.addon.network_description || {}; @@ -147,12 +175,14 @@ class HassioAddonNetwork extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); if (this.addon?.state === "started") { - await suggestAddonRestart(this, this.hass, this.addon); + await suggestAddonRestart(this, this.hass, this.supervisor, this.addon); } } catch (err) { - this._error = `Failed to set addon network configuration, ${extractApiErrorMessage( - err - )}`; + this._error = this.supervisor.localize( + "addon.failed_to_reset", + "error", + extractApiErrorMessage(err) + ); } button.progress = false; @@ -181,12 +211,14 @@ class HassioAddonNetwork extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); if (this.addon?.state === "started") { - await suggestAddonRestart(this, this.hass, this.addon); + await suggestAddonRestart(this, this.hass, this.supervisor, this.addon); } } catch (err) { - this._error = `Failed to set addon network configuration, ${extractApiErrorMessage( - err - )}`; + this._error = this.supervisor.localize( + "addon.failed_to_save", + "error", + extractApiErrorMessage(err) + ); } button.progress = false; } diff --git a/hassio/src/addon-view/documentation/hassio-addon-documentation-tab.ts b/hassio/src/addon-view/documentation/hassio-addon-documentation-tab.ts index 3d604e03bb..0cb15c1842 100644 --- a/hassio/src/addon-view/documentation/hassio-addon-documentation-tab.ts +++ b/hassio/src/addon-view/documentation/hassio-addon-documentation-tab.ts @@ -1,3 +1,4 @@ +import "../../../../src/components/ha-card"; import { css, CSSResult, @@ -19,11 +20,14 @@ import "../../../../src/layouts/hass-loading-screen"; import { haStyle } from "../../../../src/resources/styles"; import { HomeAssistant } from "../../../../src/types"; import { hassioStyle } from "../../resources/hassio-style"; +import { Supervisor } from "../../../../src/data/supervisor/supervisor"; @customElement("hassio-addon-documentation-tab") class HassioAddonDocumentationDashboard extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ attribute: false }) public supervisor!: Supervisor; + @property({ attribute: false }) public addon?: HassioAddonDetails; @internalProperty() private _error?: string; @@ -81,9 +85,11 @@ class HassioAddonDocumentationDashboard extends LitElement { this.addon!.slug ); } catch (err) { - this._error = `Failed to get addon documentation, ${extractApiErrorMessage( - err - )}`; + this._error = this.supervisor.localize( + "addon.documentation.get_logs", + "error", + extractApiErrorMessage(err) + ); } } } diff --git a/hassio/src/addon-view/hassio-addon-dashboard.ts b/hassio/src/addon-view/hassio-addon-dashboard.ts index 291a5de2c0..c1c4942816 100644 --- a/hassio/src/addon-view/hassio-addon-dashboard.ts +++ b/hassio/src/addon-view/hassio-addon-dashboard.ts @@ -79,7 +79,6 @@ class HassioAddonDashboard extends LitElement { const addonTabs: PageNavigation[] = [ { - name: "Info", translationKey: "addon.panel.info", path: `/hassio/addon/${this.addon.slug}/info`, iconPath: mdiInformationVariant, @@ -88,7 +87,6 @@ class HassioAddonDashboard extends LitElement { if (this.addon.documentation) { addonTabs.push({ - name: "Documentation", translationKey: "addon.panel.documentation", path: `/hassio/addon/${this.addon.slug}/documentation`, iconPath: mdiFileDocument, @@ -98,13 +96,11 @@ class HassioAddonDashboard extends LitElement { if (this.addon.version) { addonTabs.push( { - name: "Configuration", translationKey: "addon.panel.configuration", path: `/hassio/addon/${this.addon.slug}/config`, iconPath: mdiCogs, }, { - name: "Log", translationKey: "addon.panel.log", path: `/hassio/addon/${this.addon.slug}/logs`, iconPath: mdiMathLog, diff --git a/hassio/src/addon-view/info/hassio-addon-info.ts b/hassio/src/addon-view/info/hassio-addon-info.ts index cfdd2fb439..88d4e92a19 100644 --- a/hassio/src/addon-view/info/hassio-addon-info.ts +++ b/hassio/src/addon-view/info/hassio-addon-info.ts @@ -79,63 +79,6 @@ const STAGE_ICON = { deprecated: mdiExclamationThick, }; -const PERMIS_DESC = { - stage: { - title: "Add-on Stage", - description: `Add-ons can have one of three stages:\n\n **Stable**: These are add-ons ready to be used in production.\n\n **Experimental**: These may contain bugs, and may be unfinished.\n\n **Deprecated**: These add-ons will no longer receive any updates.`, - }, - rating: { - title: "Add-on Security Rating", - description: - "Home Assistant provides a security rating to each of the add-ons, which indicates the risks involved when using this add-on. The more access an add-on requires on your system, the lower the score, thus raising the possible security risks.\n\nA score is on a scale from 1 to 6. Where 1 is the lowest score (considered the most insecure and highest risk) and a score of 6 is the highest score (considered the most secure and lowest risk).", - }, - host_network: { - title: "Host Network", - description: - "Add-ons usually run in their own isolated network layer, which prevents them from accessing the network of the host operating system. In some cases, this network isolation can limit add-ons in providing their services and therefore, the isolation can be lifted by the add-on author, giving the add-on full access to the network capabilities of the host machine. This gives the add-on more networking capabilities but lowers the security, hence, the security rating of the add-on will be lowered when this option is used by the add-on.", - }, - homeassistant_api: { - title: "Home Assistant API Access", - description: - "This add-on is allowed to access your running Home Assistant instance directly via the Home Assistant API. This mode handles authentication for the add-on as well, which enables an add-on to interact with Home Assistant without the need for additional authentication tokens.", - }, - full_access: { - title: "Full Hardware Access", - description: - "This add-on is given full access to the hardware of your system, by request of the add-on author. Access is comparable to the privileged mode in Docker. Since this opens up possible security risks, this feature impacts the add-on security score negatively.\n\nThis level of access is not granted automatically and needs to be confirmed by you. To do this, you need to disable the protection mode on the add-on manually. Only disable the protection mode if you know, need AND trust the source of this add-on.", - }, - hassio_api: { - title: "Supervisor API Access", - description: - "The add-on was given access to the Supervisor API, by request of the add-on author. By default, the add-on can access general version information of your system. When the add-on requests 'manager' or 'admin' level access to the API, it will gain access to control multiple parts of your Home Assistant system. This permission is indicated by this badge and will impact the security score of the addon negatively.", - }, - docker_api: { - title: "Full Docker Access", - description: - "The add-on author has requested the add-on to have management access to the Docker instance running on your system. This mode gives the add-on full access and control to your entire Home Assistant system, which adds security risks, and could damage your system when misused. Therefore, this feature impacts the add-on security score negatively.\n\nThis level of access is not granted automatically and needs to be confirmed by you. To do this, you need to disable the protection mode on the add-on manually. Only disable the protection mode if you know, need AND trust the source of this add-on.", - }, - host_pid: { - title: "Host Processes Namespace", - description: - "Usually, the processes the add-on runs, are isolated from all other system processes. The add-on author has requested the add-on to have access to the system processes running on the host system instance, and allow the add-on to spawn processes on the host system as well. This mode gives the add-on full access and control to your entire Home Assistant system, which adds security risks, and could damage your system when misused. Therefore, this feature impacts the add-on security score negatively.\n\nThis level of access is not granted automatically and needs to be confirmed by you. To do this, you need to disable the protection mode on the add-on manually. Only disable the protection mode if you know, need AND trust the source of this add-on.", - }, - apparmor: { - title: "AppArmor", - description: - "AppArmor ('Application Armor') is a Linux kernel security module that restricts add-ons capabilities like network access, raw socket access, and permission to read, write, or execute specific files.\n\nAdd-on authors can provide their security profiles, optimized for the add-on, or request it to be disabled. If AppArmor is disabled, it will raise security risks and therefore, has a negative impact on the security score of the add-on.", - }, - auth_api: { - title: "Home Assistant Authentication", - description: - "An add-on can authenticate users against Home Assistant, allowing add-ons to give users the possibility to log into applications running inside add-ons, using their Home Assistant username/password. This badge indicates if the add-on author requests this capability.", - }, - ingress: { - title: "Ingress", - description: - "This add-on is using Ingress to embed its interface securely into Home Assistant.", - }, -}; - @customElement("hassio-addon-info") class HassioAddonInfo extends LitElement { @property({ type: Boolean }) public narrow!: boolean; @@ -162,11 +105,11 @@ class HassioAddonInfo extends LitElement { : undefined; const metrics = [ { - description: "Add-on CPU Usage", + description: this.supervisor.localize("addon.dashboard.cpu_usage"), value: this._metrics?.cpu_percent, }, { - description: "Add-on RAM Usage", + description: this.supervisor.localize("addon.dashboard.ram_usage"), value: this._metrics?.memory_percent, tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString( this._metrics?.memory_limit @@ -176,14 +119,28 @@ class HassioAddonInfo extends LitElement { return html` ${this.addon.update_available ? html` - +
@@ -194,29 +151,32 @@ class HassioAddonInfo extends LitElement { ) ? html`

- This add-on is not compatible with the processor of - your device or the operating system you have installed - on your device. + ${this.supervisor.localize( + "addon.dashboard.not_available_arch" + )}

` : html`

- You are running Home Assistant - ${this.supervisor.core.version}, to update to this - version of the add-on you need at least version - ${addonStoreInfo.homeassistant} of Home Assistant + ${this.supervisor.localize( + "addon.dashboard.not_available_arch", + "core_version_installed", + this.supervisor.core.version, + "core_version_needed", + addonStoreInfo.homeassistant + )}

` : ""}
- Update + ${this.supervisor.localize("common.update")} ${this.addon.changelog ? html` - Changelog + ${this.supervisor.localize("addon.dashboard.changelog")} ` : ""} @@ -227,12 +187,19 @@ class HassioAddonInfo extends LitElement { ${!this.addon.protected ? html` -

Warning: Protection mode is disabled!

+

${this.supervisor.localize( + "addon.dashboard.protection_mode.title" + )} +

- Protection mode on this add-on is disabled! This gives the add-on full access to the entire system, which adds security risks, and could damage your system when used incorrectly. Only disable the protection mode if you know, need AND trust the source of this add-on. + ${this.supervisor.localize("addon.dashboard.protection_mode.content")}
- Enable Protection mode + + ${this.supervisor.localize( + "addon.dashboard.protection_mode.enable" + )} +
@@ -249,14 +216,18 @@ class HassioAddonInfo extends LitElement { ${this._computeIsRunning ? html` ` : html` @@ -270,21 +241,29 @@ class HassioAddonInfo extends LitElement { ? html` Current version: ${this.addon.version}
- (changelog) + ( + ${this.supervisor.localize("addon.dashboard.changelog")} )
` - : html`Changelog`} + : html` + ${this.supervisor.localize("addon.dashboard.changelog")} + `}
${this.addon.description}.
- Visit - - ${this.addon.name} page - for details. + ${this.supervisor.localize( + "addon.dashboard.visit_addon_page", + "name", + html` + ${this.addon.name} + ` + )}
@@ -305,7 +284,9 @@ class HassioAddonInfo extends LitElement { })} @click=${this._showMoreInfo} id="stage" - label="stage" + .label=${this.supervisor.localize( + "addon.dashboard.capability.label.stage" + )} description="" > @@ -343,7 +326,9 @@ class HassioAddonInfo extends LitElement { @@ -355,7 +340,9 @@ class HassioAddonInfo extends LitElement { @@ -367,8 +354,12 @@ class HassioAddonInfo extends LitElement { @@ -379,7 +370,9 @@ class HassioAddonInfo extends LitElement { @@ -391,7 +384,9 @@ class HassioAddonInfo extends LitElement { @@ -404,7 +399,9 @@ class HassioAddonInfo extends LitElement { @click=${this._showMoreInfo} class=${this._computeApparmorClassName} id="apparmor" - label="apparmor" + .label=${this.supervisor.localize( + "addon.dashboard.capability.label.apparmor" + )} description="" > @@ -416,7 +413,9 @@ class HassioAddonInfo extends LitElement { @@ -428,7 +427,9 @@ class HassioAddonInfo extends LitElement { - Start on boot + ${this.supervisor.localize( + "addon.dashboard.option.boot.title" + )} - Make the add-on start during a system boot + ${this.supervisor.localize( + "addon.dashboard.option.boot.description" + )} - Watchdog + ${this.supervisor.localize( + "addon.dashboard.option.watchdog.title" + )} - This will start the add-on if it crashes + ${this.supervisor.localize( + "addon.dashboard.option.boot.description" + )} - Auto update + ${this.supervisor.localize( + "addon.dashboard.option.auto_update.title" + )} - Auto update the add-on when there is a new - version available + ${this.supervisor.localize( + "addon.dashboard.option.boot.description" + )} ` : ""} - ${this.addon.ingress + ${!this._computeCannotIngressSidebar && this.addon.ingress ? html` - Show in sidebar + ${this.supervisor.localize( + "addon.dashboard.option.ingress_panel.title" + )} - ${this._computeCannotIngressSidebar - ? "This option requires Home Assistant 0.92 or later." - : "Add this add-on to your sidebar"} + ${this.supervisor.localize( + "addon.dashboard.option.ingress_panel.description" + )} @@ -521,10 +534,14 @@ class HassioAddonInfo extends LitElement { ? html` - Protection mode + ${this.supervisor.localize( + "addon.dashboard.option.protected.title" + )} - Blocks elevated system access from the add-on + ${this.supervisor.localize( + "addon.dashboard.option.protected.description" + )} - Hostname + ${this.supervisor.localize("addon.dashboard.hostname")} ${this.addon.hostname} @@ -569,17 +586,20 @@ class HassioAddonInfo extends LitElement { ) ? html`

- This add-on is not compatible with the processor of your - device or the operating system you have installed on your - device. + ${this.supervisor.localize( + "addon.dashboard.not_available_arch" + )}

` : html`

- You are running Home Assistant - ${this.supervisor.core.version}, to install this add-on you - need at least version ${addonStoreInfo!.homeassistant} of - Home Assistant + ${this.supervisor.localize( + "addon.dashboard.not_available_version", + "core_version_installed", + this.supervisor.core.version, + "core_version_needed", + addonStoreInfo!.homeassistant + )}

` : ""} @@ -593,18 +613,18 @@ class HassioAddonInfo extends LitElement { class="warning" @click=${this._stopClicked} > - Stop + ${this.supervisor.localize("addon.dashboard.stop")} - Restart + ${this.supervisor.localize("addon.dashboard.restart")} ` : html` - Start + ${this.supervisor.localize("addon.dashboard.start")} ` : html` @@ -612,7 +632,7 @@ class HassioAddonInfo extends LitElement { .disabled=${!this.addon.available} @click=${this._installClicked} > - Install + ${this.supervisor.localize("addon.dashboard.install")} `}
@@ -627,7 +647,9 @@ class HassioAddonInfo extends LitElement { rel="noopener" > - Open web UI + ${this.supervisor.localize( + "addon.dashboard.open_web_ui" + )} ` @@ -635,7 +657,9 @@ class HassioAddonInfo extends LitElement { ${this._computeShowIngressUI ? html` - Open web UI + ${this.supervisor.localize( + "addon.dashboard.open_web_ui" + )} ` : ""} @@ -643,7 +667,7 @@ class HassioAddonInfo extends LitElement { class="warning" @click=${this._uninstallClicked} > - Uninstall + ${this.supervisor.localize("addon.dashboard.uninstall")} ${this.addon.build ? html` @@ -652,7 +676,7 @@ class HassioAddonInfo extends LitElement { .hass=${this.hass} .path="hassio/addons/${this.addon.slug}/rebuild" > - Rebuild + ${this.supervisor.localize("addon.dashboard.rebuild")} ` : ""}` @@ -712,8 +736,21 @@ class HassioAddonInfo extends LitElement { private _showMoreInfo(ev): void { const id = ev.currentTarget.id; showHassioMarkdownDialog(this, { - title: PERMIS_DESC[id].title, - content: PERMIS_DESC[id].description, + title: this.supervisor.localize(`addon.dashboard.capability.${id}.title`), + content: + id === "stage" + ? this.supervisor.localize( + `addon.dashboard.capability.${id}.description`, + "icon_stable", + ``, + "icon_experimental", + ``, + "icon_deprecated", + `` + ) + : this.supervisor.localize( + `addon.dashboard.capability.${id}.description` + ), }); } @@ -766,9 +803,11 @@ class HassioAddonInfo extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); } catch (err) { - this._error = `Failed to set addon option, ${extractApiErrorMessage( - err - )}`; + this._error = this.supervisor.localize( + "addon.failed_to_save", + "error", + extractApiErrorMessage(err) + ); } } @@ -786,9 +825,11 @@ class HassioAddonInfo extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); } catch (err) { - this._error = `Failed to set addon option, ${extractApiErrorMessage( - err - )}`; + this._error = this.supervisor.localize( + "addon.failed_to_save", + "error", + extractApiErrorMessage(err) + ); } } @@ -806,9 +847,11 @@ class HassioAddonInfo extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); } catch (err) { - this._error = `Failed to set addon option, ${extractApiErrorMessage( - err - )}`; + this._error = this.supervisor.localize( + "addon.failed_to_save", + "error", + extractApiErrorMessage(err) + ); } } @@ -826,9 +869,11 @@ class HassioAddonInfo extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); } catch (err) { - this._error = `Failed to set addon security option, ${extractApiErrorMessage( - err - )}`; + this._error = this.supervisor.localize( + "addon.failed_to_save", + "error", + extractApiErrorMessage(err) + ); } } @@ -846,9 +891,11 @@ class HassioAddonInfo extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); } catch (err) { - this._error = `Failed to set addon option, ${extractApiErrorMessage( - err - )}`; + this._error = this.supervisor.localize( + "addon.failed_to_save", + "error", + extractApiErrorMessage(err) + ); } } @@ -859,12 +906,14 @@ class HassioAddonInfo extends LitElement { this.addon.slug ); showHassioMarkdownDialog(this, { - title: "Changelog", + title: this.supervisor.localize("addon.dashboard.changelog"), content, }); } catch (err) { showAlertDialog(this, { - title: "Failed to get addon changelog", + title: this.supervisor.localize( + "addon.dashboard.action_error.get_changelog" + ), text: extractApiErrorMessage(err), }); } @@ -884,7 +933,7 @@ class HassioAddonInfo extends LitElement { fireEvent(this, "hass-api-called", eventdata); } catch (err) { showAlertDialog(this, { - title: "Failed to install addon", + title: this.supervisor.localize("addon.dashboard.action_error.install"), text: extractApiErrorMessage(err), }); } @@ -905,7 +954,7 @@ class HassioAddonInfo extends LitElement { fireEvent(this, "hass-api-called", eventdata); } catch (err) { showAlertDialog(this, { - title: "Failed to stop addon", + title: this.supervisor.localize("addon.dashboard.action_error.stop"), text: extractApiErrorMessage(err), }); } @@ -926,7 +975,7 @@ class HassioAddonInfo extends LitElement { fireEvent(this, "hass-api-called", eventdata); } catch (err) { showAlertDialog(this, { - title: "Failed to restart addon", + title: this.supervisor.localize("addon.dashboard.action_error.restart"), text: extractApiErrorMessage(err), }); } @@ -947,11 +996,15 @@ class HassioAddonInfo extends LitElement { ); if (!validate.valid) { await showConfirmationDialog(this, { - title: "Failed to start addon - configuration validation failed!", + title: this.supervisor.localize( + "addon.dashboard.action_error.start_invalid_config" + ), text: validate.message.split(" Got ")[0], confirm: () => this._openConfiguration(), - confirmText: "Go to configuration", - dismissText: "Cancel", + confirmText: this.supervisor.localize( + "addon.dashboard.action_error.go_to_config" + ), + dismissText: this.supervisor.localize("common.cancel"), }); button.progress = false; return; @@ -976,7 +1029,7 @@ class HassioAddonInfo extends LitElement { fireEvent(this, "hass-api-called", eventdata); } catch (err) { showAlertDialog(this, { - title: "Failed to start addon", + title: this.supervisor.localize("addon.dashboard.action_error.start"), text: extractApiErrorMessage(err), }); } @@ -1014,7 +1067,9 @@ class HassioAddonInfo extends LitElement { fireEvent(this, "hass-api-called", eventdata); } catch (err) { showAlertDialog(this, { - title: "Failed to uninstall addon", + title: this.supervisor.localize( + "addon.dashboard.action_error.uninstall" + ), text: extractApiErrorMessage(err), }); } diff --git a/hassio/src/addon-view/log/hassio-addon-log-tab.ts b/hassio/src/addon-view/log/hassio-addon-log-tab.ts index 54bace246b..7b93e38a5b 100644 --- a/hassio/src/addon-view/log/hassio-addon-log-tab.ts +++ b/hassio/src/addon-view/log/hassio-addon-log-tab.ts @@ -9,6 +9,7 @@ import { } from "lit-element"; import "../../../../src/components/ha-circular-progress"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; +import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { haStyle } from "../../../../src/resources/styles"; import { HomeAssistant } from "../../../../src/types"; import { hassioStyle } from "../../resources/hassio-style"; @@ -18,6 +19,8 @@ import "./hassio-addon-logs"; class HassioAddonLogDashboard extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ attribute: false }) public supervisor!: Supervisor; + @property({ attribute: false }) public addon?: HassioAddonDetails; protected render(): TemplateResult { @@ -28,6 +31,7 @@ class HassioAddonLogDashboard extends LitElement {
diff --git a/hassio/src/addon-view/log/hassio-addon-logs.ts b/hassio/src/addon-view/log/hassio-addon-logs.ts index 314dc29c09..c380c0b92e 100644 --- a/hassio/src/addon-view/log/hassio-addon-logs.ts +++ b/hassio/src/addon-view/log/hassio-addon-logs.ts @@ -15,6 +15,7 @@ import { HassioAddonDetails, } from "../../../../src/data/hassio/addon"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; +import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { haStyle } from "../../../../src/resources/styles"; import { HomeAssistant } from "../../../../src/types"; import "../../components/hassio-ansi-to-html"; @@ -24,6 +25,8 @@ import { hassioStyle } from "../../resources/hassio-style"; class HassioAddonLogs extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ attribute: false }) public supervisor!: Supervisor; + @property({ attribute: false }) public addon!: HassioAddonDetails; @internalProperty() private _error?: string; @@ -48,7 +51,9 @@ class HassioAddonLogs extends LitElement { : ""}
- Refresh + + ${this.supervisor.localize("common.refresh")} +
`; @@ -76,7 +81,11 @@ class HassioAddonLogs extends LitElement { try { this._content = await fetchHassioAddonLogs(this.hass, this.addon.slug); } catch (err) { - this._error = `Failed to get addon logs, ${extractApiErrorMessage(err)}`; + this._error = this.supervisor.localize( + "addon.logs.get_logs", + "error", + extractApiErrorMessage(err) + ); } } diff --git a/hassio/src/components/hassio-card-content.ts b/hassio/src/components/hassio-card-content.ts index 1123aa9f5a..02d3fc7e1b 100644 --- a/hassio/src/components/hassio-card-content.ts +++ b/hassio/src/components/hassio-card-content.ts @@ -44,7 +44,7 @@ class HassioCardContent extends LitElement { ${this.iconImage ? html`
- +
` diff --git a/hassio/src/components/supervisor-metric.ts b/hassio/src/components/supervisor-metric.ts index b0af0fd9a2..116b137e0f 100644 --- a/hassio/src/components/supervisor-metric.ts +++ b/hassio/src/components/supervisor-metric.ts @@ -26,7 +26,7 @@ class SupervisorMetric extends LitElement { ${this.description} -
+
${roundedValue}% diff --git a/hassio/src/dashboard/hassio-update.ts b/hassio/src/dashboard/hassio-update.ts index c991a2bf12..e0c3a2f155 100644 --- a/hassio/src/dashboard/hassio-update.ts +++ b/hassio/src/dashboard/hassio-update.ts @@ -69,7 +69,7 @@ export class HassioUpdate extends LitElement {

${this.supervisor.localize( - "dashboard.update_available", + "common.update_available", "count", updatesAvailable )} @@ -211,7 +211,7 @@ export class HassioUpdate extends LitElement { !ignoredStatusCodes.has(err.status_code) ) { showAlertDialog(this, { - title: this.supervisor.localize("error.update_failed"), + title: this.supervisor.localize("common.error.update_failed"), text: extractApiErrorMessage(err), }); } diff --git a/hassio/src/dialogs/network/dialog-hassio-network.ts b/hassio/src/dialogs/network/dialog-hassio-network.ts index 6a8deb6088..64aadb1597 100644 --- a/hassio/src/dialogs/network/dialog-hassio-network.ts +++ b/hassio/src/dialogs/network/dialog-hassio-network.ts @@ -35,6 +35,7 @@ import { updateNetworkInterface, WifiConfiguration, } from "../../../../src/data/hassio/network"; +import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { showAlertDialog, showConfirmationDialog, @@ -51,6 +52,8 @@ export class DialogHassioNetwork extends LitElement implements HassDialog { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ attribute: false }) public supervisor!: Supervisor; + @internalProperty() private _accessPoints?: AccessPoints; @internalProperty() private _curTabIndex = 0; @@ -73,7 +76,8 @@ export class DialogHassioNetwork extends LitElement this._params = params; this._dirty = false; this._curTabIndex = 0; - this._interfaces = params.network.interfaces.sort((a, b) => { + this.supervisor = params.supervisor; + this._interfaces = params.supervisor.network.interfaces.sort((a, b) => { return a.primary > b.primary ? -1 : 1; }); this._interface = { ...this._interfaces[this._curTabIndex] }; @@ -104,7 +108,7 @@ export class DialogHassioNetwork extends LitElement
- Network settings + ${this.supervisor.localize("dialog.network.title")} @@ -139,7 +143,13 @@ export class DialogHassioNetwork extends LitElement ? html` ${this._interface?.wifi?.ssid - ? html`

Connected to: ${this._interface?.wifi?.ssid}

` + ? html`

+ ${this.supervisor.localize( + "dialog.network.connected_to", + "ssid", + this._interface?.wifi?.ssid + )} +

` : ""} ` - : "Scan for accesspoints"} + : this.supervisor.localize("dialog.network.scan_ap")} ${this._accessPoints && this._accessPoints.accesspoints && @@ -181,7 +191,11 @@ export class DialogHassioNetwork extends LitElement ${this._wifiConfiguration ? html`
- + - + - + - If you are changing the Wi-Fi, IP or gateway addresses, you might - lose the connection! + ${this.supervisor.localize("dialog.network.warning")}
` : ""}
- + + ${this._processing ? html` ` - : "Save"} + : this.supervisor.localize("common.save")}
`; } @@ -285,7 +310,9 @@ export class DialogHassioNetwork extends LitElement outlined >
- + - + - + { if (this._dirty) { const confirm = await showConfirmationDialog(this, { - text: - "You have unsaved changes, these will get lost if you change tabs, do you want to continue?", - confirmText: "yes", - dismissText: "no", + text: this.supervisor.localize("dialog.network.unsaved"), + confirmText: this.supervisor.localize("common.yes"), + dismissText: this.supervisor.localize("common.no"), }); if (!confirm) { this.requestUpdate("_interface"); diff --git a/hassio/src/dialogs/network/show-dialog-network.ts b/hassio/src/dialogs/network/show-dialog-network.ts index 632f42c624..edf21edff5 100644 --- a/hassio/src/dialogs/network/show-dialog-network.ts +++ b/hassio/src/dialogs/network/show-dialog-network.ts @@ -1,9 +1,9 @@ import { fireEvent } from "../../../../src/common/dom/fire_event"; -import { NetworkInfo } from "../../../../src/data/hassio/network"; +import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import "./dialog-hassio-network"; export interface HassioNetworkDialogParams { - network: NetworkInfo; + supervisor: Supervisor; loadData: () => Promise; } diff --git a/hassio/src/dialogs/registries/dialog-hassio-registries.ts b/hassio/src/dialogs/registries/dialog-hassio-registries.ts index fef6e3e0a8..292fab3f43 100644 --- a/hassio/src/dialogs/registries/dialog-hassio-registries.ts +++ b/hassio/src/dialogs/registries/dialog-hassio-registries.ts @@ -22,14 +22,18 @@ import { fetchHassioDockerRegistries, removeHassioDockerRegistry, } from "../../../../src/data/hassio/docker"; +import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box"; import { haStyle, haStyleDialog } from "../../../../src/resources/styles"; import type { HomeAssistant } from "../../../../src/types"; +import { RegistriesDialogParams } from "./show-dialog-registries"; @customElement("dialog-hassio-registries") class HassioRegistriesDialog extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ attribute: false }) public supervisor!: Supervisor; + @property({ attribute: false }) private _registries?: { registry: string; username: string; @@ -55,8 +59,8 @@ class HassioRegistriesDialog extends LitElement { .heading=${createCloseHeading( this.hass, this._addingRegistry - ? "Add New Docker Registry" - : "Manage Docker Registries" + ? this.supervisor.localize("dialog.registries.title_add") + : this.supervisor.localize("dialog.registries.title_manage") )} >
@@ -66,7 +70,9 @@ class HassioRegistriesDialog extends LitElement { @value-changed=${this._inputChanged} class="flex-auto" name="registry" - label="Registry" + .label=${this.supervisor.localize( + "dialog.registries.registry" + )} required auto-validate > @@ -74,7 +80,9 @@ class HassioRegistriesDialog extends LitElement { @value-changed=${this._inputChanged} class="flex-auto" name="username" - label="Username" + .label=${this.supervisor.localize( + "dialog.registries.username" + )} required auto-validate > @@ -82,7 +90,9 @@ class HassioRegistriesDialog extends LitElement { @value-changed=${this._inputChanged} class="flex-auto" name="password" - label="Password" + .label=${this.supervisor.localize( + "dialog.registries.password" + )} type="password" required auto-validate @@ -94,7 +104,7 @@ class HassioRegistriesDialog extends LitElement { )} @click=${this._addNewRegistry} > - Add registry + ${this.supervisor.localize("dialog.registries.add_registry")} ` : html`${this._registries?.length @@ -103,11 +113,16 @@ class HassioRegistriesDialog extends LitElement { ${entry.registry} Username: ${entry.username}${this.supervisor.localize( + "dialog.registries.username" + )}: + ${entry.username} @@ -118,11 +133,17 @@ class HassioRegistriesDialog extends LitElement { }) : html` - No registries configured + ${this.supervisor.localize( + "dialog.registries.no_registries" + )} `} - Add new registry + ${this.supervisor.localize( + "dialog.registries.add_new_registry" + )} `}
@@ -134,8 +155,9 @@ class HassioRegistriesDialog extends LitElement { this[`_${target.name}`] = target.value; } - public async showDialog(_dialogParams: any): Promise { + public async showDialog(dialogParams: RegistriesDialogParams): Promise { this._opened = true; + this.supervisor = dialogParams.supervisor; await this._loadRegistries(); await this.updateComplete; } @@ -178,7 +200,7 @@ class HassioRegistriesDialog extends LitElement { this._addingRegistry = false; } catch (err) { showAlertDialog(this, { - title: "Failed to add registry", + title: this.supervisor.localize("dialog.registries.failed_to_add"), text: extractApiErrorMessage(err), }); } @@ -192,7 +214,7 @@ class HassioRegistriesDialog extends LitElement { await this._loadRegistries(); } catch (err) { showAlertDialog(this, { - title: "Failed to remove registry", + title: this.supervisor.localize("dialog.registries.failed_to_remove"), text: extractApiErrorMessage(err), }); } diff --git a/hassio/src/dialogs/registries/show-dialog-registries.ts b/hassio/src/dialogs/registries/show-dialog-registries.ts index 15cf88c075..fd62edc7a7 100644 --- a/hassio/src/dialogs/registries/show-dialog-registries.ts +++ b/hassio/src/dialogs/registries/show-dialog-registries.ts @@ -1,10 +1,18 @@ import { fireEvent } from "../../../../src/common/dom/fire_event"; +import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import "./dialog-hassio-registries"; -export const showRegistriesDialog = (element: HTMLElement): void => { +export interface RegistriesDialogParams { + supervisor: Supervisor; +} + +export const showRegistriesDialog = ( + element: HTMLElement, + dialogParams: RegistriesDialogParams +): void => { fireEvent(element, "show-dialog", { dialogTag: "dialog-hassio-registries", dialogImport: () => import("./dialog-hassio-registries"), - dialogParams: {}, + dialogParams, }); }; diff --git a/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts b/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts index 2d8d028cdf..35ea16957a 100644 --- a/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts +++ b/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts @@ -18,7 +18,7 @@ import { } from "lit-element"; import memoizeOne from "memoize-one"; import "../../../../src/components/ha-circular-progress"; -import "../../../../src/components/ha-dialog"; +import { createCloseHeading } from "../../../../src/components/ha-dialog"; import "../../../../src/components/ha-svg-icon"; import { fetchHassioAddonsInfo, @@ -26,6 +26,7 @@ import { } from "../../../../src/data/hassio/addon"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { setSupervisorOption } from "../../../../src/data/hassio/supervisor"; +import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { haStyle, haStyleDialog } from "../../../../src/resources/styles"; import type { HomeAssistant } from "../../../../src/types"; import { HassioRepositoryDialogParams } from "./show-dialog-repositories"; @@ -34,6 +35,8 @@ import { HassioRepositoryDialogParams } from "./show-dialog-repositories"; class HassioRepositoriesDialog extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ attribute: false }) public supervisor!: Supervisor; + @property({ attribute: false }) private _repos: HassioAddonRepository[] = []; @property({ attribute: false }) @@ -47,9 +50,11 @@ class HassioRepositoriesDialog extends LitElement { @internalProperty() private _error?: string; - public async showDialog(_dialogParams: any): Promise { - this._dialogParams = _dialogParams; - this._repos = _dialogParams.repos; + public async showDialog( + dialogParams: HassioRepositoryDialogParams + ): Promise { + this._dialogParams = dialogParams; + this.supervisor = dialogParams.supervisor; this._opened = true; await this.updateComplete; } @@ -66,14 +71,19 @@ class HassioRepositoriesDialog extends LitElement { ); protected render(): TemplateResult { - const repositories = this._filteredRepositories(this._repos); + const repositories = this._filteredRepositories( + this.supervisor.addon.repositories + ); return html` ${this._error ? html`
${this._error}
` : ""}
@@ -88,7 +98,9 @@ class HassioRepositoriesDialog extends LitElement { @@ -105,13 +117,13 @@ class HassioRepositoriesDialog extends LitElement { ${this._prosessing ? html`` - : "Add"} + : this.supervisor.localize("dialog.repositories.add")}
diff --git a/hassio/src/dialogs/repositories/show-dialog-repositories.ts b/hassio/src/dialogs/repositories/show-dialog-repositories.ts index 893d4fd4e6..63b3611de8 100644 --- a/hassio/src/dialogs/repositories/show-dialog-repositories.ts +++ b/hassio/src/dialogs/repositories/show-dialog-repositories.ts @@ -1,9 +1,9 @@ import { fireEvent } from "../../../../src/common/dom/fire_event"; -import { HassioAddonRepository } from "../../../../src/data/hassio/addon"; +import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import "./dialog-hassio-repositories"; export interface HassioRepositoryDialogParams { - repos: HassioAddonRepository[]; + supervisor: Supervisor; loadData: () => Promise; } diff --git a/hassio/src/dialogs/suggestAddonRestart.ts b/hassio/src/dialogs/suggestAddonRestart.ts index de4343b379..12b8d383d7 100644 --- a/hassio/src/dialogs/suggestAddonRestart.ts +++ b/hassio/src/dialogs/suggestAddonRestart.ts @@ -4,6 +4,7 @@ import { restartHassioAddon, } from "../../../src/data/hassio/addon"; import { extractApiErrorMessage } from "../../../src/data/hassio/common"; +import { Supervisor } from "../../../src/data/supervisor/supervisor"; import { showAlertDialog, showConfirmationDialog, @@ -13,20 +14,25 @@ import { HomeAssistant } from "../../../src/types"; export const suggestAddonRestart = async ( element: LitElement, hass: HomeAssistant, + supervisor: Supervisor, addon: HassioAddonDetails ): Promise => { const confirmed = await showConfirmationDialog(element, { - title: addon.name, - text: "Do you want to restart the add-on with your changes?", - confirmText: "restart add-on", - dismissText: "no", + title: supervisor.localize("common.restart_name", "name", addon.name), + text: supervisor.localize("dialog.restart_addon.text"), + confirmText: supervisor.localize("dialog.restart_addon.confirm_text"), + dismissText: supervisor.localize("common.cancel"), }); if (confirmed) { try { await restartHassioAddon(hass, addon.slug); } catch (err) { showAlertDialog(element, { - title: "Failed to restart", + title: supervisor.localize( + "common.failed_to_restart_name", + "name", + addon.name + ), text: extractApiErrorMessage(err), }); } diff --git a/hassio/src/hassio-my-redirect.ts b/hassio/src/hassio-my-redirect.ts index 389816b435..33992391a0 100644 --- a/hassio/src/hassio-my-redirect.ts +++ b/hassio/src/hassio-my-redirect.ts @@ -19,6 +19,7 @@ import { } from "../../src/panels/my/ha-panel-my"; import { navigate } from "../../src/common/navigate"; import { HomeAssistant, Route } from "../../src/types"; +import { Supervisor } from "../../src/data/supervisor/supervisor"; const REDIRECTS: Redirects = { supervisor_logs: { @@ -48,7 +49,9 @@ const REDIRECTS: Redirects = { class HassioMyRedirect extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public route!: Route; + @property({ attribute: false }) public supervisor!: Supervisor; + + @property({ attribute: false }) public route!: Route; @internalProperty() public _error?: TemplateResult | string; @@ -58,15 +61,17 @@ class HassioMyRedirect extends LitElement { const redirect = REDIRECTS[path]; if (!redirect) { - this._error = html`This redirect is not supported by your Home Assistant - instance. Check the - My Home Assistant FAQ - for the supported redirects and the version they where introduced.`; + ${this.supervisor.localize("my.faq_link")} + ` + ); return; } @@ -74,7 +79,7 @@ class HassioMyRedirect extends LitElement { try { url = this._createRedirectUrl(redirect); } catch (err) { - this._error = "An unknown error occured"; + this._error = this.supervisor.localize("my.error"); return; } diff --git a/hassio/src/snapshots/hassio-snapshots.ts b/hassio/src/snapshots/hassio-snapshots.ts index 82e6d1c877..8081525d5f 100644 --- a/hassio/src/snapshots/hassio-snapshots.ts +++ b/hassio/src/snapshots/hassio-snapshots.ts @@ -55,8 +55,8 @@ import { hassioStyle } from "../resources/hassio-style"; interface CheckboxItem { slug: string; - name: string; checked: boolean; + name?: string; } @customElement("hassio-snapshots") @@ -84,13 +84,12 @@ class HassioSnapshots extends LitElement { @internalProperty() private _folderList: CheckboxItem[] = [ { slug: "homeassistant", - name: "Home Assistant configuration", checked: true, }, - { slug: "ssl", name: "SSL", checked: true }, - { slug: "share", name: "Share", checked: true }, - { slug: "media", name: "Media", checked: true }, - { slug: "addons/local", name: "Local add-ons", checked: true }, + { slug: "ssl", checked: true }, + { slug: "share", checked: true }, + { slug: "media", checked: true }, + { slug: "addons/local", checked: true }, ]; @internalProperty() private _error = ""; @@ -123,50 +122,50 @@ class HassioSnapshots extends LitElement { - Reload + ${this.supervisor.localize("common.reload")} ${atLeastVersion(this.hass.config.version, 0, 116) ? html` - Upload snapshot + ${this.supervisor.localize("snapshot.upload_snapshot")} ` : ""}

- Create Snapshot + ${this.supervisor.localize("snapshot.create_snapshot")}

- Snapshots allow you to easily backup and restore all data of your - Home Assistant instance. + ${this.supervisor.localize("snapshot.description")}

- Type: + ${this.supervisor.localize("snapshot.type")}: - Full snapshot + ${this.supervisor.localize("snapshot.full_snapshot")} - Partial snapshot + ${this.supervisor.localize("snapshot.partial_snapshot")} ${this._snapshotType === "full" ? undefined : html` - Folders: + ${this.supervisor.localize("snapshot.folders")}: ${this._folderList.map( (folder, idx) => html` - ${folder.name} + ${this.supervisor.localize( + `snapshot.folder.${folder.slug}` + )} ` )} - Add-ons: + ${this.supervisor.localize("snapshot.addons")}: ${this._addonList.map( (addon, idx) => html` - Password protection + ${this.supervisor.localize("snapshot.password_protection")} ${this._snapshotHasPassword ? html` - Create + ${this.supervisor.localize("snapshot.create")}
-

Available Snapshots

+

${this.supervisor.localize("snapshot.available_snapshots")}

${this._snapshots === undefined ? undefined @@ -236,7 +241,7 @@ class HassioSnapshots extends LitElement { ? html`
- You don't have any snapshots yet. + ${this.supervisor.localize("snapshot.no_snapshots")}
` @@ -337,8 +342,12 @@ class HassioSnapshots extends LitElement { private async _createSnapshot(ev: CustomEvent): Promise { if (this.supervisor.info.state !== "running") { await showAlertDialog(this, { - title: "Could not create snapshot", - text: `Creating a snapshot is not possible right now because the system is in ${this.supervisor.info.state} state.`, + title: this.supervisor.localize("snapshot.could_not_create"), + text: this.supervisor.localize( + "snapshot.create_blocked_not_running", + "state", + this.supervisor.info.state + ), }); } const button = ev.currentTarget as any; @@ -346,7 +355,7 @@ class HassioSnapshots extends LitElement { this._error = ""; if (this._snapshotHasPassword && !this._snapshotPassword.length) { - this._error = "Please enter a password."; + this._error = this.supervisor.localize("snapshot.enter_password"); button.progress = false; return; } @@ -395,7 +404,9 @@ class HassioSnapshots extends LitElement { private _computeDetails(snapshot: HassioSnapshot) { const type = - snapshot.type === "full" ? "Full snapshot" : "Partial snapshot"; + snapshot.type === "full" + ? this.supervisor.localize("snapshot.full_snapshot") + : this.supervisor.localize("snapshot.partial_snapshot"); return snapshot.protected ? `${type}, password protected` : type; } diff --git a/hassio/src/system/hassio-core-info.ts b/hassio/src/system/hassio-core-info.ts index 21ab1c84b6..a50c7dc4fa 100644 --- a/hassio/src/system/hassio-core-info.ts +++ b/hassio/src/system/hassio-core-info.ts @@ -43,11 +43,11 @@ class HassioCoreInfo extends LitElement { protected render(): TemplateResult | void { const metrics = [ { - description: "Core CPU Usage", + description: this.supervisor.localize("system.core.cpu_usage"), value: this._metrics?.cpu_percent, }, { - description: "Core RAM Usage", + description: this.supervisor.localize("system.core.ram_usage"), value: this._metrics?.memory_percent, tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString( this._metrics?.memory_limit @@ -61,7 +61,7 @@ class HassioCoreInfo extends LitElement {
- Version + ${this.supervisor.localize("common.version")} core-${this.supervisor.core.version} @@ -69,7 +69,7 @@ class HassioCoreInfo extends LitElement { - Newest Version + ${this.supervisor.localize("common.newest_version")} core-${this.supervisor.core.version_latest} @@ -77,10 +77,10 @@ class HassioCoreInfo extends LitElement { ${this.supervisor.core.update_available ? html` - Update + ${this.supervisor.localize("common.update")} ` : ""} @@ -104,9 +104,13 @@ class HassioCoreInfo extends LitElement { slot="primaryAction" class="warning" @click=${this._coreRestart} - title="Restart Home Assistant Core" + .title=${this.supervisor.localize( + "common.restart_name", + "name", + "Core" + )} > - Restart Core + ${this.supervisor.localize("common.restart_name", "name", "Core")}
@@ -126,10 +130,18 @@ class HassioCoreInfo extends LitElement { button.progress = true; const confirmed = await showConfirmationDialog(this, { - title: "Restart Home Assistant Core", - text: "Are you sure you want to restart Home Assistant Core", - confirmText: "restart", - dismissText: "cancel", + title: this.supervisor.localize( + "confirm.restart.title", + "name", + "Home Assistant Core" + ), + text: this.supervisor.localize( + "confirm.restart.text", + "name", + "Home Assistant Core" + ), + confirmText: this.supervisor.localize("common.restart"), + dismissText: this.supervisor.localize("common.cancel"), }); if (!confirmed) { @@ -142,7 +154,11 @@ class HassioCoreInfo extends LitElement { } catch (err) { if (this.hass.connection.connected) { showAlertDialog(this, { - title: "Failed to restart Home Assistant Core", + title: this.supervisor.localize( + "common.failed_to_restart_name", + "name", + "Home AssistantCore" + ), text: extractApiErrorMessage(err), }); } diff --git a/hassio/src/system/hassio-host-info.ts b/hassio/src/system/hassio-host-info.ts index dd4b7932ca..a6b6bf3884 100644 --- a/hassio/src/system/hassio-host-info.ts +++ b/hassio/src/system/hassio-host-info.ts @@ -65,7 +65,7 @@ class HassioHostInfo extends LitElement { const metrics = [ { - description: "Used Space", + description: this.supervisor.localize("system.host.used_space"), value: this._getUsedSpace( this.supervisor.host.disk_used, this.supervisor.host.disk_total @@ -80,14 +80,13 @@ class HassioHostInfo extends LitElement { ${this.supervisor.host.features.includes("hostname") ? html` - Hostname + ${this.supervisor.localize("system.host.hostname")} ${this.supervisor.host.hostname} @@ -96,14 +95,13 @@ class HassioHostInfo extends LitElement { ${this.supervisor.host.features.includes("network") ? html` - IP Address + ${this.supervisor.localize("system.host.ip_address")} ${primaryIpAddress} @@ -112,18 +110,15 @@ class HassioHostInfo extends LitElement { - Operating System + ${this.supervisor.localize("system.host.operating_system")} ${this.supervisor.host.operating_system} ${this.supervisor.os.update_available ? html` - - Update + + ${this.supervisor.localize("commmon.update")} ` : ""} @@ -131,7 +126,7 @@ class HassioHostInfo extends LitElement { ${!this.supervisor.host.features.includes("hassos") ? html` - Docker version + ${this.supervisor.localize("system.host.docker_version")} ${this.supervisor.info.docker} @@ -141,7 +136,7 @@ class HassioHostInfo extends LitElement { ${this.supervisor.host.deployment ? html` - Deployment + ${this.supervisor.localize("system.host.deployment")} ${this.supervisor.host.deployment} @@ -154,7 +149,9 @@ class HassioHostInfo extends LitElement { this.supervisor.host.disk_life_time >= 10 ? html` - eMMC Lifetime Used + ${this.supervisor.localize( + "system.host.emmc_lifetime_used" + )} ${this.supervisor.host.disk_life_time - 10}% - @@ -177,23 +174,18 @@ class HassioHostInfo extends LitElement {
${this.supervisor.host.features.includes("reboot") ? html` - - Reboot Host + + ${this.supervisor.localize("system.host.reboot_host")} ` : ""} ${this.supervisor.host.features.includes("shutdown") ? html` - Shutdown Host + ${this.supervisor.localize("system.host.shutdown_host")} ` : ""} @@ -205,14 +197,12 @@ class HassioHostInfo extends LitElement { - - Hardware + + ${this.supervisor.localize("system.host.hardware")} ${this.supervisor.host.features.includes("hassos") - ? html` - Import from USB + ? html` + ${this.supervisor.localize("system.host.import_from_usb")} ` : ""} @@ -251,12 +241,14 @@ class HassioHostInfo extends LitElement { try { const content = await fetchHassioHardwareInfo(this.hass); showHassioMarkdownDialog(this, { - title: "Hardware", + title: this.supervisor.localize("system.host.hardware"), content: `
${safeDump(content, { indent: 2 })}
`, }); } catch (err) { showAlertDialog(this, { - title: "Failed to get hardware list", + title: this.supervisor.localize( + "system.host.failed_to_get_hardware_list" + ), text: extractApiErrorMessage(err), }); } @@ -267,10 +259,10 @@ class HassioHostInfo extends LitElement { button.progress = true; const confirmed = await showConfirmationDialog(this, { - title: "Reboot", - text: "Are you sure you want to reboot the host?", - confirmText: "reboot host", - dismissText: "no", + title: this.supervisor.localize("system.host.reboot_host"), + text: this.supervisor.localize("system.host.confirm_reboot"), + confirmText: this.supervisor.localize("system.host.reboot_host"), + dismissText: this.supervisor.localize("common.cancel"), }); if (!confirmed) { @@ -284,7 +276,7 @@ class HassioHostInfo extends LitElement { // Ignore connection errors, these are all expected if (err.status_code && !ignoredStatusCodes.has(err.status_code)) { showAlertDialog(this, { - title: "Failed to reboot", + title: this.supervisor.localize("system.host.failed_to_reboot"), text: extractApiErrorMessage(err), }); } @@ -297,10 +289,10 @@ class HassioHostInfo extends LitElement { button.progress = true; const confirmed = await showConfirmationDialog(this, { - title: "Shutdown", - text: "Are you sure you want to shutdown the host?", - confirmText: "shutdown host", - dismissText: "no", + title: this.supervisor.localize("system.host.shutdown_host"), + text: this.supervisor.localize("system.host.confirm_shutdown"), + confirmText: this.supervisor.localize("system.host.shutdown_host"), + dismissText: this.supervisor.localize("common.cancel"), }); if (!confirmed) { @@ -314,7 +306,7 @@ class HassioHostInfo extends LitElement { // Ignore connection errors, these are all expected if (err.status_code && !ignoredStatusCodes.has(err.status_code)) { showAlertDialog(this, { - title: "Failed to shutdown", + title: this.supervisor.localize("system.host.failed_to_shutdown"), text: extractApiErrorMessage(err), }); } @@ -327,9 +319,19 @@ class HassioHostInfo extends LitElement { button.progress = true; const confirmed = await showConfirmationDialog(this, { - title: "Update", - text: "Are you sure you want to update the OS?", - confirmText: "update os", + title: this.supervisor.localize( + "confirm.update.title", + "name", + "Home Assistant Operating System" + ), + text: this.supervisor.localize( + "confirm.update.text", + "name", + "Home Assistant Operating System", + "version", + this.supervisor.os.version_latest + ), + confirmText: this.supervisor.localize("common.update"), dismissText: "no", }); @@ -344,7 +346,11 @@ class HassioHostInfo extends LitElement { } catch (err) { if (this.hass.connection.connected) { showAlertDialog(this, { - title: "Failed to update", + title: this.supervisor.localize( + "common.failed_to_update_name", + "name", + "Home Assistant Operating System" + ), text: extractApiErrorMessage(err), }); } @@ -354,7 +360,7 @@ class HassioHostInfo extends LitElement { private async _changeNetworkClicked(): Promise { showNetworkDialog(this, { - network: this.supervisor.network!, + supervisor: this.supervisor, loadData: () => this._loadData(), }); } @@ -362,10 +368,11 @@ class HassioHostInfo extends LitElement { private async _changeHostnameClicked(): Promise { const curHostname: string = this.supervisor.host.hostname; const hostname = await showPromptDialog(this, { - title: "Change Hostname", - inputLabel: "Please enter a new hostname:", + title: this.supervisor.localize("system.host.change_hostname"), + inputLabel: this.supervisor.localize("system.host.new_hostname"), inputType: "string", defaultValue: curHostname, + confirmText: this.supervisor.localize("common.update"), }); if (hostname && hostname !== curHostname) { @@ -376,7 +383,7 @@ class HassioHostInfo extends LitElement { }); } catch (err) { showAlertDialog(this, { - title: "Setting hostname failed", + title: this.supervisor.localize("system.host.failed_to_set_hostname"), text: extractApiErrorMessage(err), }); } @@ -391,7 +398,9 @@ class HassioHostInfo extends LitElement { }); } catch (err) { showAlertDialog(this, { - title: "Failed to import from USB", + title: this.supervisor.localize( + "system.host.failed_to_import_from_usb" + ), text: extractApiErrorMessage(err), }); } diff --git a/hassio/src/system/hassio-supervisor-info.ts b/hassio/src/system/hassio-supervisor-info.ts index 3afc373e0b..a9b25a7ab7 100644 --- a/hassio/src/system/hassio-supervisor-info.ts +++ b/hassio/src/system/hassio-supervisor-info.ts @@ -37,54 +37,24 @@ import { documentationUrl } from "../../../src/util/documentation-url"; import "../components/supervisor-metric"; import { hassioStyle } from "../resources/hassio-style"; -const UNSUPPORTED_REASON = { - container: { - title: "Containers known to cause issues", - url: "/more-info/unsupported/container", - }, - dbus: { title: "DBUS", url: "/more-info/unsupported/dbus" }, - docker_configuration: { - title: "Docker Configuration", - url: "/more-info/unsupported/docker_configuration", - }, - docker_version: { - title: "Docker Version", - url: "/more-info/unsupported/docker_version", - }, - job_conditions: { - title: "Ignored job conditions", - url: "/more-info/unsupported/job_conditions", - }, - lxc: { title: "LXC", url: "/more-info/unsupported/lxc" }, - network_manager: { - title: "Network Manager", - url: "/more-info/unsupported/network_manager", - }, - os: { title: "Operating System", url: "/more-info/unsupported/os" }, - privileged: { - title: "Supervisor is not privileged", - url: "/more-info/unsupported/privileged", - }, - systemd: { title: "Systemd", url: "/more-info/unsupported/systemd" }, +const UNSUPPORTED_REASON_URL = { + container: "/more-info/unsupported/container", + dbus: "/more-info/unsupported/dbus", + docker_configuration: "/more-info/unsupported/docker_configuration", + docker_version: "/more-info/unsupported/docker_version", + job_conditions: "/more-info/unsupported/job_conditions", + lxc: "/more-info/unsupported/lxc", + network_manager: "/more-info/unsupported/network_manager", + os: "/more-info/unsupported/os", + privileged: "/more-info/unsupported/privileged", + systemd: "/more-info/unsupported/systemd", }; -const UNHEALTHY_REASON = { - privileged: { - title: "Supervisor is not privileged", - url: "/more-info/unsupported/privileged", - }, - supervisor: { - title: "Supervisor was not able to update", - url: "/more-info/unhealthy/supervisor", - }, - setup: { - title: "Setup of the Supervisor failed", - url: "/more-info/unhealthy/setup", - }, - docker: { - title: "The Docker environment is not working properly", - url: "/more-info/unhealthy/docker", - }, +const UNHEALTHY_REASON_URL = { + privileged: "/more-info/unsupported/privileged", + supervisor: "/more-info/unhealthy/supervisor", + setup: "/more-info/unhealthy/setup", + docker: "/more-info/unhealthy/docker", }; @customElement("hassio-supervisor-info") @@ -98,11 +68,11 @@ class HassioSupervisorInfo extends LitElement { protected render(): TemplateResult | void { const metrics = [ { - description: "Supervisor CPU Usage", + description: this.supervisor.localize("system.supervisor.cpu_usage"), value: this._metrics?.cpu_percent, }, { - description: "Supervisor RAM Usage", + description: this.supervisor.localize("system.supervisor.ram_usage"), value: this._metrics?.memory_percent, tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString( this._metrics?.memory_limit @@ -115,7 +85,7 @@ class HassioSupervisorInfo extends LitElement {
- Version + ${this.supervisor.localize("common.version")} supervisor-${this.supervisor.supervisor.version} @@ -123,7 +93,7 @@ class HassioSupervisorInfo extends LitElement { - Newest Version + ${this.supervisor.localize("common.newest_version")} supervisor-${this.supervisor.supervisor.version_latest} @@ -131,17 +101,19 @@ class HassioSupervisorInfo extends LitElement { ${this.supervisor.supervisor.update_available ? html` - Update + ${this.supervisor.localize("common.update")} ` : ""} - Channel + ${this.supervisor.localize("system.supervisor.channel")} ${this.supervisor.supervisor.channel} @@ -150,18 +122,26 @@ class HassioSupervisorInfo extends LitElement { ? html` - Leave beta channel + ${this.supervisor.localize( + "system.supervisor.leave_beta_action" + )} ` : this.supervisor.supervisor.channel === "stable" ? html` - Join beta channel + ${this.supervisor.localize( + "system.supervisor.join_beta_action" + )} ` : ""} @@ -170,16 +150,20 @@ class HassioSupervisorInfo extends LitElement { ${this.supervisor.supervisor.supported ? html` - Share Diagnostics + ${this.supervisor.localize( + "system.supervisor.share_diagnostics" + )}
- Share crash reports and diagnostic information. + ${this.supervisor.localize( + "system.supervisor.share_diagnostics_description" + )}
` : html`
- You are running an unsupported installation. + ${this.supervisor.localize( + "system.supervisor.unsupported_title" + )}
`} ${!this.supervisor.supervisor.healthy ? html`
- Your installation is running in an unhealthy state. + ${this.supervisor.localize( + "system.supervisor.unhealthy_title" + )}
- +
`; diff --git a/src/translations/en.json b/src/translations/en.json index 1df9ec42d1..4c4ee6591a 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3420,24 +3420,215 @@ }, "supervisor": { "addon": { + "failed_to_reset": "Failed to reset add-on configuration, {error}", + "failed_to_save": "Failed to save add-on configuration, {error}", + + "state": { + "installed": "Add-on is installed", + "not_installed": "Add-on is not installed", + "not_available": "Add-on is not available on your system" + }, "panel": { "configuration": "Configuration", "documentation": "Documentation", "info": "Info", "log": "Log" + }, + "configuration": { + "no_configuration": "This add-on does not expose configuration for you to mess with...", + "audio": { + "header": "Audio", + "default": "Default", + "input": "Input", + "output": "Output" + }, + "options": { + "header": "Options", + "edit_in_ui": "Edit in UI", + "edit_in_yaml": "Edit in YAML", + "invalid_yaml": "Invalid YAML", + "show_unused_optional": "Show unused optional configuration options" + }, + "network": { + "container": "Container", + "disabled": "Disabled", + "header": "Network", + "host": "Host" + } + }, + "dashboard": { + "changelog": "Changelog", + "cpu_usage": "Add-on CPU Usage", + "ram_usage": "Add-on RAM Usage", + "hostname": "Hostname", + "new_update_available": "{name} {version} is available", + "not_available_arch": "This add-on is not compatible with the processor of your device or the operating system you have installed on your device.", + "not_available_version": "You are unning Home Assistant {core_version_installed}, to update to this version of the add-on you need at least version {core_version_needed} of Home Assistan", + "visit_addon_page": "Visit the {name} page for more detals", + "restart": "restart", + "start": "start", + "stop": "stop", + "install": "install", + "uninstall": "uninstall", + "rebuild": "rebuild", + "open_web_ui": "Open web UI", + + "protection_mode": { + "title": "Warning: Protection mode is disabled!", + "content": "Protection mode on this add-on is disabled! This gives the add-on full access to the entire system, which adds security risks, and could damage your system when used incorrectly. Only disable the protection mode if you know, need AND trust the source of this add-on.", + "enable": "Enable Protection mode" + }, + + "capability": { + "stage": { + "title": "Add-on Stage", + "description": "Add-ons can have one of three stages:\n\n{icon_stable} **Stable**: These are add-ons ready to be used in production.\n\n{icon_experimental} **Experimental**: These may contain bugs, and may be unfinished.\n\n{icon_deprecated} **Deprecated**: These add-ons will no longer receive any updates." + }, + "rating": { + "title": "Add-on Security Rating", + "description": "Home Assistant provides a security rating to each of the add-ons, which indicates the risks involved when using this add-on. The more access an add-on requires on your system, the lower the score, thus raising the possible security risks.\n\nA score is on a scale from 1 to 6. Where 1 is the lowest score (considered the most insecure and highest risk) and a score of 6 is the highest score (considered the most secure and lowest risk)." + }, + "host_network": { + "title": "Host Network", + "description": "Add-ons usually run in their own isolated network layer, which prevents them from accessing the network of the host operating system. In some cases, this network isolation can limit add-ons in providing their services and therefore, the isolation can be lifted by the add-on author, giving the add-on full access to the network capabilities of the host machine. This gives the add-on more networking capabilities but lowers the security, hence, the security rating of the add-on will be lowered when this option is used by the add-on." + }, + "homeassistant_api": { + "title": "Home Assistant API Access", + "description": "This add-on is allowed to access your running Home Assistant instance directly via the Home Assistant API. This mode handles authentication for the add-on as well, which enables an add-on to interact with Home Assistant without the need for additional authentication tokens." + }, + "full_access": { + "title": "Full Hardware Access", + "description": "This add-on is given full access to the hardware of your system, by request of the add-on author. Access is comparable to the privileged mode in Docker. Since this opens up possible security risks, this feature impacts the add-on security score negatively.\n\nThis level of access is not granted automatically and needs to be confirmed by you. To do this, you need to disable the protection mode on the add-on manually. Only disable the protection mode if you know, need AND trust the source of this add-on." + }, + "hassio_api": { + "title": "Supervisor API Access", + "description": "The add-on was given access to the Supervisor API, by request of the add-on author. By default, the add-on can access general version information of your system. When the add-on requests 'manager' or 'admin' level access to the API, it will gain access to control multiple parts of your Home Assistant system. This permission is indicated by this badge and will impact the security score of the add-on negatively." + }, + "docker_api": { + "title": "Full Docker Access", + "description": "The add-on author has requested the add-on to have management access to the Docker instance running on your system. This mode gives the add-on full access and control to your entire Home Assistant system, which adds security risks, and could damage your system when misused. Therefore, this feature impacts the add-on security score negatively.\n\nThis level of access is not granted automatically and needs to be confirmed by you. To do this, you need to disable the protection mode on the add-on manually. Only disable the protection mode if you know, need AND trust the source of this add-on." + }, + "host_pid": { + "title": "Host Processes Namespace", + "description": "Usually, the processes the add-on runs, are isolated from all other system processes. The add-on author has requested the add-on to have access to the system processes running on the host system instance, and allow the add-on to spawn processes on the host system as well. This mode gives the add-on full access and control to your entire Home Assistant system, which adds security risks, and could damage your system when misused. Therefore, this feature impacts the add-on security score negatively.\n\nThis level of access is not granted automatically and needs to be confirmed by you. To do this, you need to disable the protection mode on the add-on manually. Only disable the protection mode if you know, need AND trust the source of this add-on." + }, + "apparmor": { + "title": "AppArmor", + "description": "AppArmor ('Application Armor') is a Linux kernel security module that restricts add-ons capabilities like network access, raw socket access, and permission to read, write, or execute specific files.\n\nAdd-on authors can provide their security profiles, optimized for the add-on, or request it to be disabled. If AppArmor is disabled, it will raise security risks and therefore, has a negative impact on the security score of the add-on." + }, + "auth_api": { + "title": "Home Assistant Authentication", + "description": "An add-on can authenticate users against Home Assistant, allowing add-ons to give users the possibility to log into applications running inside add-ons, using their Home Assistant username/password. This badge indicates if the add-on author requests this capability." + }, + "ingress": { + "title": "Ingress", + "description": "This add-on is using Ingress to embed its interface securely into Home Assistant." + }, + "label": { + "stage": "stage", + "rating": "rating", + "hardware": "hardware", + "host": "host", + "hass": "hass", + "hassio": "hassio", + "docker": "docker", + "host_pid": "host pid", + "apparmor": "apparmor", + "auth": "auth", + "ingress": "ingress" + }, + "role": { + "manager": "manager", + "default": "default", + "homeassistant": "homeassistant", + "backup": "backup", + "admin": "admin" + } + }, + + "option": { + "boot": { + "title": "Start on boot", + "description": "Make the add-on start during a system boot" + }, + "watchdog": { + "title": "Watchdog", + "description": "This will start the add-on if it crashes" + }, + "auto_update": { + "title": "Auto update", + "description": "Auto update the add-on when there is a new version available" + }, + "ingress_panel": { + "title": "Show in sidebar", + "description": "Add this add-on to your sidebar" + }, + "protected": { + "title": "Protection mode", + "description": "Blocks elevated system access from the add-on" + } + }, + + "action_error": { + "uninstall": "Failed to uninstall add-on", + "install": "Failed to install add-on", + "stop": "Failed to stop add-on", + "restart": "Failed to restart add-on", + "start": "Failed to start add-on", + "go_to_config": "Failed to start add-on - configuration validation failed!", + "start_invalid_config": "Go to configuration", + "validate_config": "Failed to validate add-on configuration", + "get_changelog": "Failed to get add-on changelog" + } + }, + "documentation": { + "get_documentation": "Failed to get add-on documentation, {error}" + }, + "logs": { + "get_logs": "Failed to get add-on logs, {error}" } }, "common": { - "cancel": "Cancel", + "cancel": "[%key:ui::common::cancel%]", + "yes": "[%key:ui::common::yes%]", + "no": "[%key:ui::common::no%]", + "description": "Description", + "failed_to_restart_name": "Failed to restart {name}", + "failed_to_update_name": "Failed to update {name}", + "learn_more": "Learn more", + "new_version_available": "New version available", "newest_version": "Newest Version", + "refresh": "[%key:ui::common::refresh%]", "release_notes": "Release notes", + "reload": "Reload", + "reset_defaults": "Reset to defaults", + "reset_options": "Reset options", + "restart_name": "Restart {name}", + "restart": "Restart", + "running_version": "You are currently running version {version}", + "save": "Save", + "show_more": "Show more information about this", + "update_available": "{count, plural,\n one {Update}\n other {{count} Updates}\n} pending", "update": "Update", - "version": "Version" + "version": "Version", + + "error": { + "unknown": "Unknown error", + "update_failed": "Update failed" + } }, "confirm": { "update": { - "title": "Update ${name}", + "title": "Update {name}", "text": "Are you sure you want to update {name} to version {version}?" + }, + "restart": { + "title": "[%key:supervisor::common::restart_name%]", + "text": "Are you sure you want to restart {name}?" + }, + "reset_options": { + "title": "Reset options", + "text": "Are you sure you want to reset all your options?" } }, "dashboard": { @@ -3445,18 +3636,172 @@ "addon_running": "Add-on is running", "addon_stopped": "Add-on is stopped", "addons": "Add-ons", - "no_addons": "You don't have any add-ons installed yet. Head over to the add-on store to get started!", - "update_available": "{count, plural,\n one {Update}\n other {{count} Updates}\n} pending" + "no_addons": "You don't have any add-ons installed yet. Head over to the add-on store to get started!" }, - "error": { - "unknown": "Unknown error", - "update_failed": "Update failed" + "store": { + "missing_addons": "Missing add-ons? Enable advanced mode in your user profile page", + "no_results_found": "No results found in {repository}.", + "registries": "Registries", + "repositories": "Repositories" }, "panel": { "dashboard": "Dashboard", "snapshots": "Snapshots", "store": "Add-on Store", "system": "System" + }, + "my": { + "not_supported": "[%key:ui::panel::my::not_supported%]", + "faq_link": "[%key:ui::panel::my::faq_link%]", + "error": "[%key:ui::panel::my::error%]" + }, + "system": { + "log": { + "log_provider": "Log Provider", + "get_logs": "Failed to get {provider} logs, {error}" + }, + "supervisor": { + "cpu_usage": "Supervisor CPU Usage", + "ram_usage": "Supervisor RAM Usage", + "failed_to_set_option": "Failed to set Supervisor option", + "failed_to_reload": "Failed to reload the Supervisor", + "failed_to_update": "Failed to update the Supervisor", + "unsupported_title": "You are running an unsupported installation", + "unsupported_description": "Below is a list of issues found with your installation, click on the links to learn how you can resolve the issues.", + "unhealthy_title": "Your installation is unhealthy", + "unhealthy_description": "Running an unhealthy installation will cause issues. Below is a list of issues found with your installation, click on the links to learn how you can resolve the issues.", + "update_supervisor": "Update the Supervisor", + "channel": "Channel", + "leave_beta_action": "Leave beta channel", + "leave_beta_description": "Get stable updates for Home Assistant, Supervisor and host", + "join_beta_action": "Join beta channel", + "join_beta_description": "Get beta updates for Home Assistant (RCs), Supervisor and host", + "share_diagnostics": "Share Diagnostics", + "share_diagnostics_description": "Share crash reports and diagnostic information.", + "reload_supervisor": "Reload Supervisor", + "warning": "WARNING", + "beta_warning": "Beta releases are for testers and early adopters and can contain unstable code changes", + "beta_backup": "Make sure you have backups of your data before you activate this feature.", + "beta_release_items": "This includes beta releases for:", + "beta_join_confirm": "Do you want to join the beta channel?", + "share_diagonstics_title": "Help Improve Home Assistant", + "share_diagonstics_description": "Would you want to automatically share crash reports and diagnostic information when the Supervisor encounters unexpected errors? {line_break} This will allow us to fix the problems, the information is only accessible to the Home Assistant Core team and will not be shared with others.{line_break} The data does not include any private/sensitive information and you can disable this in settings at any time you want.", + "unsupported_reason": { + "container": "Containers known to cause issues", + "dbus": "DBUS", + "docker_configuration": "Docker Configuration", + "docker_version": "Docker Version", + "job_conditions": "Ignored job conditions", + "lxc": "LXC", + "network_manager": "Network Manager", + "os": "Operating System", + "privileged": "Supervisor is not privileged", + "systemd": "Systemd" + }, + "unhealthy_reason": { + "privileged": "Supervisor is not privileged", + "supervisor": "Supervisor was not able to update", + "setup": "Setup of the Supervisor failed", + "docker": "The Docker environment is not working properly" + } + }, + "host": { + "failed_to_get_hardware_list": "Failed to get hardware list", + "failed_to_reboot": "Failed to reboot the host", + "failed_to_shutdown": "Failed to shutdown the host", + "failed_to_set_hostname": "Setting hostname failed", + "failed_to_import_from_usb": "Failed to import from USB", + "used_space": "Used space", + "hostname": "Hostname", + "change_hostname": "Change Hostname", + "new_hostname": "Please enter a new hostname:", + "ip_address": "IP Address", + "change": "Change", + "operating_system": "Operating System", + "docker_version": "Docker version", + "deployment": "Deployment", + "emmc_lifetime_used": "eMMC Lifetime Used", + "reboot_host": "Reboot host", + "confirm_reboot": "Are you sure you want to reboot the host?", + "confirm_shutdown": "Are you sure you want to shutdown the host?", + "shutdown_host": "Shutdown host", + "hardware": "Hardware", + "import_from_usb": "Import from USB" + }, + "core": { + "cpu_usage": "Core CPU Usage", + "ram_usage": "Core RAM Usage" + } + }, + "snapshot": { + "description": "Snapshots allow you to easily backup and restore all data of your Home Assistant instance.", + "available_snapshots": "Available Snapshots", + "no_snapshots": "You don't have any snapshots yet.", + "create_blocked_not_running": "Creating a snapshot is not possible right now because the system is in {state} state.", + "could_not_create": "Could not create snapshot", + "upload_snapshot": "Upload snapshot", + "create_snapshot": "Create snapshot", + "create": "Create", + "name": "Name", + "type": "Type", + "security": "Security", + "full_snapshot": "Full snapshot", + "partial_snapshot": "Partial snapshot", + "addons": "Add-ons", + "folders": "Folders", + "password": "Password", + "password_protection": "Password protection", + "password_protected": "password protected", + "enter_password": "Please enter a password.", + + "folder": { + "homeassistant": "Home Assistant configuration", + "ssl": "SSL", + "share": "Share", + "media": "Media", + "addons/local": "Local add-ons" + } + }, + "dialog": { + "network": { + "title": "Network settings", + "connected_to": "Connected to {ssid}", + "scan_ap": "Scan for accesspoints", + "open": "Open", + "wep": "WEP", + "wpa": "wpa-psk", + "warning": "If you are changing the Wi-Fi, IP or gateway addresses, you might lose the connection!", + "static": "Static", + "dhcp": "DHCP", + "disabled": "Disabled", + "ip_netmask": "IP address/Netmask", + "gateway": "Gateway address", + "dns_servers": "DNS Servers", + "unsaved": "You have unsaved changes, these will get lost if you change tabs, do you want to continue?", + "failed_to_change": "Failed to change network settings" + }, + "registries": { + "title_add": "Add New Container Registry", + "title_manage": "Manage Container Registries", + "registry": "Registry", + "username": "Username", + "password": "Password", + "no_registries": "No registries configured", + "add_registry": "Add registry", + "add_new_registry": "Add new registry", + "remove": "Remove", + "failed_to_add": "Failed to add registry", + "failed_to_remove": "Failed to remove registry" + }, + "repositories": { + "title": "Manage add-on repositories", + "add": "Add", + "remove": "Remove" + }, + "restart_addon": { + "confirm_text": "Restart add-on", + "text": "Do you want to restart the add-on with your changes?" + } } } } From d8cdbac15e59bb270caa1a6f989241f744e69a83 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 2 Mar 2021 15:49:51 +0100 Subject: [PATCH 5/5] Bumped version to 20210302.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e13d3c0249..d5d36dcb34 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20210301.0", + version="20210302.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/home-assistant-polymer", author="The Home Assistant Authors",

ContainerHostDescription + ${this.supervisor.localize( + "addon.configuration.network.container" + )} + + ${this.supervisor.localize( + "addon.configuration.network.host" + )} + + ${this.supervisor.localize("common.description")} +
${item.description}${this._computeDescription(item)}