mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-13 20:36:35 +00:00
Panel translation fragments (#691)
* Move flatten to separate gulp task * Split translation fragments into separate files * Load translation fragments when switching panels * Fix gulpfile lint * Move app-location to home-assistant.html * Compute panel navigation in home-assistant.html * Only pass down panelUrl from home-assistant.html * Store panelUrl on hass * Store computed panel on hass object * Revert "Store computed panel on hass object" This reverts commit 0f150b1faa2b91588a432ab346821b633b349d1a. IMO this didn't really make the code cleaner. Just wanted to see how it would look. Keeping it here as a reverted commit in case we do want to use it.
This commit is contained in:
parent
2765c88d3f
commit
e0a63a2ee3
@ -9,7 +9,17 @@ const rename = require('gulp-rename');
|
||||
const transform = require('gulp-json-transform');
|
||||
|
||||
const inDir = 'translations';
|
||||
const outDir = 'build-translations';
|
||||
const workDir = 'build-translations';
|
||||
const fullDir = workDir + '/full';
|
||||
const coreDir = workDir + '/core';
|
||||
const outDir = workDir + '/output';
|
||||
|
||||
// Panel translations which should be split from the core translations. These
|
||||
// should mirror the fragment definitions in polymer.json, so that we load
|
||||
// additional resources at equivalent points.
|
||||
const TRANSLATION_FRAGMENTS = [
|
||||
'shopping-list',
|
||||
];
|
||||
|
||||
const tasks = [];
|
||||
|
||||
@ -29,6 +39,20 @@ function flatten(data) {
|
||||
return recursiveFlatten('', data);
|
||||
}
|
||||
|
||||
function emptyFilter(data) {
|
||||
const newData = {};
|
||||
Object.keys(data).forEach((key) => {
|
||||
if (data[key]) {
|
||||
if (typeof (data[key]) === 'object') {
|
||||
newData[key] = emptyFilter(data[key]);
|
||||
} else {
|
||||
newData[key] = data[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
return newData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace Lokalise key placeholders with their actual values.
|
||||
*
|
||||
@ -73,7 +97,7 @@ gulp.task(taskName, function () {
|
||||
return lokalise_transform(data, data);
|
||||
}))
|
||||
.pipe(rename('translationMaster.json'))
|
||||
.pipe(gulp.dest(outDir));
|
||||
.pipe(gulp.dest(workDir));
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
@ -90,36 +114,80 @@ gulp.task(taskName, ['build-master-translation'], function () {
|
||||
// than a base translation + region.
|
||||
const tr = path.basename(file.history[0], '.json');
|
||||
const subtags = tr.split('-');
|
||||
const src = [outDir + '/translationMaster.json'];
|
||||
const src = [workDir + '/translationMaster.json'];
|
||||
for (let i = 1; i <= subtags.length; i++) {
|
||||
const lang = subtags.slice(0, i).join('-');
|
||||
src.push(inDir + '/' + lang + '.json');
|
||||
}
|
||||
return gulp.src(src)
|
||||
.pipe(transform(function (data) {
|
||||
// Polymer.AppLocalizeBehavior requires flattened json
|
||||
return flatten(data);
|
||||
}))
|
||||
.pipe(transform(function (data) {
|
||||
const newData = {};
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
// Filter out empty strings or other falsey values before merging
|
||||
if (data[key]) newData[key] = value;
|
||||
});
|
||||
return newData;
|
||||
}))
|
||||
.pipe(transform(data => emptyFilter(data)))
|
||||
.pipe(merge({
|
||||
fileName: tr + '.json',
|
||||
}))
|
||||
.pipe(minify())
|
||||
.pipe(gulp.dest(outDir));
|
||||
.pipe(gulp.dest(fullDir));
|
||||
}));
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
taskName = 'build-translation-fingerprints';
|
||||
const splitTasks = [];
|
||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
taskName = 'build-translation-fragment-' + fragment;
|
||||
gulp.task(taskName, ['build-merged-translations'], function () {
|
||||
// Return only the translations for this fragment.
|
||||
return gulp.src(fullDir + '/*.json')
|
||||
.pipe(transform(data => ({
|
||||
ui: {
|
||||
panel: {
|
||||
[fragment]: data.ui.panel[fragment],
|
||||
},
|
||||
},
|
||||
})))
|
||||
.pipe(gulp.dest(workDir + '/' + fragment));
|
||||
});
|
||||
tasks.push(taskName);
|
||||
splitTasks.push(taskName);
|
||||
});
|
||||
|
||||
taskName = 'build-translation-core';
|
||||
gulp.task(taskName, ['build-merged-translations'], function () {
|
||||
return gulp.src(outDir + '/!(translationFingerprints|translationMaster).json')
|
||||
// Remove the fragment translations from the core translation.
|
||||
return gulp.src(fullDir + '/*.json')
|
||||
.pipe(transform((data) => {
|
||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
delete data.ui.panel[fragment];
|
||||
});
|
||||
return data;
|
||||
}))
|
||||
.pipe(gulp.dest(coreDir));
|
||||
});
|
||||
tasks.push(taskName);
|
||||
splitTasks.push(taskName);
|
||||
|
||||
taskName = 'build-flattened-translations';
|
||||
gulp.task(taskName, splitTasks, function () {
|
||||
// Flatten the split versions of our translations, and move them into outDir
|
||||
return gulp.src(
|
||||
TRANSLATION_FRAGMENTS.map(fragment => workDir + '/' + fragment + '/*.json')
|
||||
.concat(coreDir + '/*.json'),
|
||||
{ base: workDir },
|
||||
)
|
||||
.pipe(transform(function (data) {
|
||||
// Polymer.AppLocalizeBehavior requires flattened json
|
||||
return flatten(data);
|
||||
}))
|
||||
.pipe(minify())
|
||||
.pipe(rename((filePath) => {
|
||||
if (filePath.dirname === 'core') {
|
||||
filePath.dirname = '';
|
||||
}
|
||||
}))
|
||||
.pipe(gulp.dest(outDir));
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
taskName = 'build-translation-fingerprints';
|
||||
gulp.task(taskName, ['build-flattened-translations'], function () {
|
||||
return gulp.src(outDir + '/**/*.json')
|
||||
.pipe(rename({
|
||||
extname: '',
|
||||
}))
|
||||
@ -130,12 +198,25 @@ gulp.task(taskName, ['build-merged-translations'], function () {
|
||||
}))
|
||||
.pipe(hash.manifest('translationFingerprints.json'))
|
||||
.pipe(transform(function (data) {
|
||||
Object.keys(data).forEach((key) => {
|
||||
data[key] = { fingerprint: data[key] };
|
||||
// After generating fingerprints of our translation files, consolidate
|
||||
// all translation fragment fingerprints under the translation name key
|
||||
const newData = {};
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
const parts = key.split('/');
|
||||
let translation = key;
|
||||
if (parts.length === 2) {
|
||||
translation = parts[1];
|
||||
}
|
||||
if (!(translation in newData)) {
|
||||
newData[translation] = {
|
||||
fingerprints: {},
|
||||
};
|
||||
}
|
||||
newData[translation].fingerprints[key] = value;
|
||||
});
|
||||
return data;
|
||||
return newData;
|
||||
}))
|
||||
.pipe(gulp.dest(outDir));
|
||||
.pipe(gulp.dest(workDir));
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
@ -143,13 +224,13 @@ taskName = 'build-translations';
|
||||
gulp.task(taskName, ['build-translation-fingerprints'], function () {
|
||||
return gulp.src([
|
||||
'src/translations/translationMetadata.json',
|
||||
outDir + '/translationFingerprints.json',
|
||||
workDir + '/translationFingerprints.json',
|
||||
])
|
||||
.pipe(merge({}))
|
||||
.pipe(transform(function (data) {
|
||||
const newData = {};
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
// Filter out empty strings or other falsey values before merging
|
||||
// Filter out translations without native name.
|
||||
if (data[key].nativeName) {
|
||||
newData[key] = data[key];
|
||||
} else {
|
||||
@ -159,9 +240,13 @@ gulp.task(taskName, ['build-translation-fingerprints'], function () {
|
||||
});
|
||||
return newData;
|
||||
}))
|
||||
.pipe(transform(data => ({
|
||||
fragments: TRANSLATION_FRAGMENTS,
|
||||
translations: data,
|
||||
})))
|
||||
.pipe(insert.wrap('<script>\nwindow.translationMetadata = ', ';\n</script>'))
|
||||
.pipe(rename('translationMetadata.html'))
|
||||
.pipe(gulp.dest(outDir));
|
||||
.pipe(gulp.dest(workDir));
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
|
@ -122,7 +122,7 @@ class HaConfigCore extends Polymer.Element {
|
||||
|
||||
computeIsTranslationLoaded(hass) {
|
||||
return hass.translationMetadata &&
|
||||
Object.keys(hass.translationMetadata).length;
|
||||
Object.keys(hass.translationMetadata.translations).length;
|
||||
}
|
||||
|
||||
computeIsThemesLoaded(hass) {
|
||||
|
@ -60,9 +60,9 @@ class HaConfigSectionTranslation extends
|
||||
if (!hass || !hass.translationMetadata) {
|
||||
return [];
|
||||
}
|
||||
return Object.keys(hass.translationMetadata).map(key => ({
|
||||
return Object.keys(hass.translationMetadata.translations).map(key => ({
|
||||
tag: key,
|
||||
nativeName: hass.translationMetadata[key].nativeName,
|
||||
nativeName: hass.translationMetadata.translations[key].nativeName,
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
<link rel="import" href="../../bower_components/paper-listbox/paper-listbox.html">
|
||||
<link rel="import" href="../../bower_components/paper-item/paper-item.html">
|
||||
|
||||
<link rel='import' href='../..//src/util/hass-mixins.html'>
|
||||
<link rel="import" href="../../src/components/ha-menu-button.html">
|
||||
<link rel="import" href="../../src/components/ha-start-voice-button.html">
|
||||
|
||||
@ -65,7 +66,7 @@
|
||||
<app-header slot="header" fixed>
|
||||
<app-toolbar>
|
||||
<ha-menu-button narrow='[[narrow]]' show-menu='[[showMenu]]'></ha-menu-button>
|
||||
<div main-title>Shopping List</div>
|
||||
<div main-title>[[haLocalize('panel', 'shopping_list')]]</div>
|
||||
<ha-start-voice-button hass='[[hass]]' can-listen='{{canListen}}'></ha-start-voice-button>
|
||||
<paper-menu-button
|
||||
horizontal-align="right"
|
||||
@ -79,7 +80,7 @@
|
||||
<paper-listbox slot="dropdown-content">
|
||||
<paper-item
|
||||
on-tap="_clearCompleted"
|
||||
>Clear completed</paper-item>
|
||||
>[[haLocalize('ui.panel.shopping-list', 'clear_completed')]]</paper-item>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
</app-toolbar>
|
||||
@ -96,7 +97,7 @@
|
||||
<paper-item-body>
|
||||
<paper-input
|
||||
id='addBox'
|
||||
placeholder="Add item"
|
||||
placeholder="[[haLocalize('ui.panel.shopping-list', 'add_item')]]"
|
||||
on-keydown='_addKeyPress'
|
||||
no-label-float
|
||||
></paper-input>
|
||||
@ -123,7 +124,7 @@
|
||||
</template>
|
||||
</paper-card>
|
||||
<div class='tip' hidden$='[[!canListen]]'>
|
||||
Tap the microphone on the top right and say "Add candy to my shopping list"
|
||||
[[haLocalize('ui.panel.shopping-list', 'microphone_tip')]]
|
||||
</div>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
@ -131,7 +132,10 @@
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
class HaPanelShoppingList extends Polymer.Element {
|
||||
/*
|
||||
* @appliesMixin window.hassMixins.LocalizeMixin
|
||||
*/
|
||||
class HaPanelShoppingList extends window.hassMixins.LocalizeMixin(Polymer.Element) {
|
||||
static get is() { return 'ha-panel-shopping-list'; }
|
||||
|
||||
static get properties() {
|
||||
|
@ -28,8 +28,7 @@ mkdir $OUTPUT_DIR_ES5/panels
|
||||
cp build-es5/panels/*.html $OUTPUT_DIR_ES5/panels
|
||||
|
||||
# Translations
|
||||
mkdir $OUTPUT_DIR/translations
|
||||
cp build-translations/*.json $OUTPUT_DIR/translations
|
||||
cp -r build-translations/output $OUTPUT_DIR/translations
|
||||
|
||||
# Local Roboto
|
||||
cp -r bower_components/font-roboto-local/fonts $OUTPUT_DIR
|
||||
@ -60,6 +59,7 @@ gzip -f -n -k -9 \
|
||||
*.js \
|
||||
./panels/*.html \
|
||||
./translations/*.json \
|
||||
./translations/*/*.json \
|
||||
./fonts/roboto/*.ttf \
|
||||
./fonts/robotomono/*.ttf
|
||||
cd ..
|
||||
|
@ -101,7 +101,7 @@
|
||||
<paper-icon-button icon='mdi:chevron-left' hidden$='[[narrow]]' on-tap='toggleMenu'></paper-icon-button>
|
||||
</app-toolbar>
|
||||
|
||||
<paper-listbox attr-for-selected='data-panel' selected='[[route.panel]]'>
|
||||
<paper-listbox attr-for-selected='data-panel' selected='[[hass.panelUrl]]'>
|
||||
<paper-icon-item on-tap='menuClicked' data-panel='states'>
|
||||
<iron-icon slot="item-icon" icon='mdi:apps'></iron-icon>
|
||||
<span class='item-text'>[[haLocalize('panel', 'states')]]</span>
|
||||
@ -171,19 +171,13 @@ class HaSidebar extends
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
menuShown: {
|
||||
type: Boolean,
|
||||
},
|
||||
|
||||
menuSelected: {
|
||||
type: String,
|
||||
},
|
||||
|
||||
narrow: Boolean,
|
||||
|
||||
route: Object,
|
||||
|
||||
panels: {
|
||||
type: Array,
|
||||
computed: 'computePanels(hass)',
|
||||
|
@ -5,11 +5,13 @@
|
||||
<link rel='import' href='./util/roboto.html'>
|
||||
<link rel='import' href='../bower_components/paper-styles/typography.html'>
|
||||
<link rel='import' href='../bower_components/iron-flex-layout/iron-flex-layout-classes.html'>
|
||||
<link rel='import' href='../bower_components/app-route/app-route.html'>
|
||||
<link rel='import' href='../bower_components/app-route/app-location.html'>
|
||||
|
||||
<!--polyfill for paper-dropdown-->
|
||||
<link rel="import" href="../bower_components/neon-animation/web-animations.html">
|
||||
|
||||
|
||||
<link rel='import' href='../build-translations/translationMetadata.html' />
|
||||
<link rel='import' href='./util/hass-translation.html'>
|
||||
<link rel='import' href='./util/hass-util.html'>
|
||||
<link rel='import' href='./util/ha-pref-storage.html'>
|
||||
@ -24,6 +26,12 @@
|
||||
<template>
|
||||
<ha-pref-storage hass='[[hass]]' id='storage'></ha-pref-storage>
|
||||
<notification-manager id='notifications' hass='[[hass]]'></notification-manager>
|
||||
<app-location route="{{route}}"></app-location>
|
||||
<app-route
|
||||
route="{{route}}"
|
||||
pattern="/:panel"
|
||||
data="{{routeData}}"
|
||||
></app-route>
|
||||
<template is='dom-if' if='[[showMain]]' restamp>
|
||||
<home-assistant-main
|
||||
on-hass-more-info='handleMoreInfo'
|
||||
@ -31,6 +39,7 @@
|
||||
on-hass-notification='handleNotification'
|
||||
on-hass-logout='handleLogout'
|
||||
hass='[[hass]]'
|
||||
route='{{route}}'
|
||||
></home-assistant-main>
|
||||
</template>
|
||||
|
||||
@ -75,6 +84,13 @@ class HomeAssistant extends Polymer.Element {
|
||||
type: Boolean,
|
||||
computed: 'computeShowMain(hass)',
|
||||
},
|
||||
route: Object,
|
||||
routeData: Object,
|
||||
panelUrl: {
|
||||
type: String,
|
||||
computed: 'computePanelUrl(routeData)',
|
||||
observer: 'panelUrlChanged',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -95,12 +111,23 @@ class HomeAssistant extends Polymer.Element {
|
||||
|| (hass && hass.connection && (!hass.states || !hass.config)));
|
||||
}
|
||||
|
||||
loadResources() {
|
||||
window.getTranslation().then((result) => {
|
||||
this._updateHass({
|
||||
language: result.language,
|
||||
resources: result.resources,
|
||||
});
|
||||
loadResources(fragment) {
|
||||
window.getTranslation(fragment).then((result) => {
|
||||
this._updateResources(result.language, result.data);
|
||||
});
|
||||
}
|
||||
|
||||
_updateResources(language, data) {
|
||||
// Update the language in hass, and update the resources with the newly
|
||||
// loaded resources. This merges the new data on top of the old data for
|
||||
// this language, so that the full translation set can be loaded across
|
||||
// multiple fragments.
|
||||
this._updateHass({
|
||||
language: language,
|
||||
resources: {
|
||||
[language]: Object.assign({}, this.hass
|
||||
&& this.hass.resources && this.hass.resources[language], data),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -130,6 +157,7 @@ class HomeAssistant extends Polymer.Element {
|
||||
states: null,
|
||||
config: null,
|
||||
themes: null,
|
||||
panelUrl: this.panelUrl,
|
||||
|
||||
// If language and resources are already loaded, don't discard them
|
||||
language: (this.hass && this.hass.language) || null,
|
||||
@ -219,6 +247,15 @@ class HomeAssistant extends Polymer.Element {
|
||||
};
|
||||
}
|
||||
|
||||
computePanelUrl(routeData) {
|
||||
return (routeData && routeData.panel) || 'states';
|
||||
}
|
||||
|
||||
panelUrlChanged(newPanelUrl) {
|
||||
this._updateHass({ panelUrl: newPanelUrl });
|
||||
this.loadTranslationFragment(newPanelUrl);
|
||||
}
|
||||
|
||||
handleConnectionPromise(prom) {
|
||||
if (!prom) return;
|
||||
|
||||
@ -268,6 +305,14 @@ class HomeAssistant extends Polymer.Element {
|
||||
this._updateHass({ selectedLanguage: event.detail.language });
|
||||
this.$.storage.storeState();
|
||||
this.loadResources();
|
||||
this.loadTranslationFragment(this.panelUrl);
|
||||
}
|
||||
|
||||
loadTranslationFragment(panelUrl) {
|
||||
if (this.hass.translationMetadata
|
||||
&& this.hass.translationMetadata.fragments.includes(panelUrl)) {
|
||||
this.loadResources(panelUrl);
|
||||
}
|
||||
}
|
||||
|
||||
_updateHass(obj) {
|
||||
|
@ -2,10 +2,7 @@
|
||||
<link rel='import' href='../../bower_components/paper-drawer-panel/paper-drawer-panel.html'>
|
||||
<link rel='import' href='../../bower_components/iron-media-query/iron-media-query.html'>
|
||||
<link rel='import' href='../../bower_components/iron-pages/iron-pages.html'>
|
||||
|
||||
<link rel='import' href='../../bower_components/app-route/app-route.html'>
|
||||
<link rel='import' href='../../bower_components/app-route/app-location.html'>
|
||||
|
||||
|
||||
<link rel='import' href='../layouts/partial-cards.html'>
|
||||
<link rel='import' href='../layouts/partial-panel-resolver.html'>
|
||||
@ -26,13 +23,6 @@
|
||||
</style>
|
||||
<more-info-dialog hass='[[hass]]'></more-info-dialog>
|
||||
<ha-url-sync hass='[[hass]]'></ha-url-sync>
|
||||
<app-location route="{{route}}"></app-location>
|
||||
<app-route
|
||||
route="{{route}}"
|
||||
pattern="/:panel"
|
||||
data="{{routeData}}"
|
||||
tail="{{routeTail}}"
|
||||
></app-route>
|
||||
<app-route
|
||||
route="{{route}}"
|
||||
pattern="/states"
|
||||
@ -47,20 +37,19 @@
|
||||
|
||||
<paper-drawer-panel id='drawer'
|
||||
force-narrow='[[computeForceNarrow(narrow, dockedSidebar)]]'
|
||||
responsive-width='0' disable-swipe='[[_computeDisableSwipe(routeData)]]'
|
||||
disable-edge-swipe='[[_computeDisableSwipe(routeData)]]'>
|
||||
responsive-width='0' disable-swipe='[[_computeDisableSwipe(hass)]]'
|
||||
disable-edge-swipe='[[_computeDisableSwipe(hass)]]'>
|
||||
<ha-sidebar
|
||||
slot="drawer"
|
||||
narrow='[[narrow]]'
|
||||
hass='[[hass]]'
|
||||
route='[[routeData]]'
|
||||
></ha-sidebar>
|
||||
|
||||
<iron-pages
|
||||
slot="main"
|
||||
attr-for-selected='id'
|
||||
fallback-selection='panel-resolver'
|
||||
selected='[[_computeSelected(routeData)]]'
|
||||
selected='[[hass.panelUrl]]'
|
||||
selected-attribute='panel-visible'
|
||||
>
|
||||
<partial-cards
|
||||
@ -96,17 +85,12 @@
|
||||
static get properties() {
|
||||
return {
|
||||
hass: Object,
|
||||
|
||||
narrow: Boolean,
|
||||
|
||||
route: {
|
||||
type: Object,
|
||||
observer: '_routeChanged',
|
||||
},
|
||||
routeData: Object,
|
||||
routeTail: Object,
|
||||
statesRouteTail: Object,
|
||||
|
||||
dockedSidebar: {
|
||||
type: Boolean,
|
||||
computed: 'computeDockedSidebar(hass)',
|
||||
@ -163,12 +147,8 @@
|
||||
return hass.dockedSidebar;
|
||||
}
|
||||
|
||||
_computeSelected(routeData) {
|
||||
return routeData.panel || 'states';
|
||||
}
|
||||
|
||||
_computeDisableSwipe(routeData) {
|
||||
return NON_SWIPABLE_PANELS.indexOf(routeData.panel) !== -1;
|
||||
_computeDisableSwipe(hass) {
|
||||
return NON_SWIPABLE_PANELS.indexOf(hass.panelUrl) !== -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
<link rel='import' href='../../bower_components/polymer/polymer-element.html'>
|
||||
<link rel='import' href='../../bower_components/app-route/app-route.html'>
|
||||
|
||||
<link rel='import' href='../util/hass-mixins.html'>
|
||||
<link rel="import" href="./hass-loading-screen.html">
|
||||
|
||||
<dom-module id='partial-panel-resolver'>
|
||||
@ -50,38 +51,28 @@ class PartialPanelResolver extends Polymer.Element {
|
||||
value: false,
|
||||
observer: 'updateAttributes',
|
||||
},
|
||||
|
||||
route: Object,
|
||||
|
||||
routeData: Object,
|
||||
|
||||
routeTail: {
|
||||
type: Object,
|
||||
observer: 'updateAttributes',
|
||||
},
|
||||
|
||||
resolved: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
errorLoading: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
|
||||
panel: {
|
||||
type: Object,
|
||||
computed: 'computeCurrentPanel(hass, routeData)',
|
||||
computed: 'computeCurrentPanel(hass)',
|
||||
observer: 'panelChanged',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
computeCurrentPanel(hass, routeData) {
|
||||
return routeData ? hass.config.panels[routeData.panel] : null;
|
||||
}
|
||||
|
||||
panelChanged(panel) {
|
||||
if (!panel) {
|
||||
if (this.$.panel.lastChild) {
|
||||
@ -123,6 +114,10 @@ class PartialPanelResolver extends Polymer.Element {
|
||||
customEl.showMenu = this.showMenu;
|
||||
customEl.route = this.routeTail;
|
||||
}
|
||||
|
||||
computeCurrentPanel(hass) {
|
||||
return hass.config.panels[hass.panelUrl];
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define(PartialPanelResolver.is, PartialPanelResolver);
|
||||
|
@ -218,5 +218,14 @@
|
||||
"home": "[%key:state::device_tracker::home%]",
|
||||
"not_home": "[%key:state::device_tracker::not_home%]"
|
||||
}
|
||||
},
|
||||
"ui": {
|
||||
"panel": {
|
||||
"shopping-list": {
|
||||
"clear_completed": "Clear completed",
|
||||
"add_item": "Add item",
|
||||
"microphone_tip": "Tap the microphone on the top right and say “Add candy to my shopping list”"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ function getActiveTranslation() {
|
||||
// report languages with specific cases.
|
||||
const lookup = {};
|
||||
/* eslint-disable no-undef */
|
||||
Object.keys(window.translationMetadata).forEach((tr) => {
|
||||
Object.keys(window.translationMetadata.translations).forEach((tr) => {
|
||||
lookup[tr.toLowerCase()] = tr;
|
||||
});
|
||||
|
||||
@ -60,16 +60,18 @@ function getActiveTranslation() {
|
||||
// when DOM is created in Polymer. Even a cache lookup creates noticable latency.
|
||||
const translations = {};
|
||||
|
||||
window.getTranslation = function (translationInput) {
|
||||
window.getTranslation = function (fragment, translationInput) {
|
||||
const translation = translationInput || getActiveTranslation();
|
||||
const metadata = window.translationMetadata[translation];
|
||||
const metadata = window.translationMetadata.translations[translation];
|
||||
if (!metadata) {
|
||||
if (translationInput !== 'en') {
|
||||
return window.getTranslation('en');
|
||||
return window.getTranslation(fragment, 'en');
|
||||
}
|
||||
return Promise.reject(new Error('Language en not found in metadata'));
|
||||
}
|
||||
const translationFingerprint = metadata.fingerprint;
|
||||
const translationFingerprint = metadata.fingerprints[
|
||||
fragment ? `${fragment}/${translation}` : translation
|
||||
];
|
||||
|
||||
// Create a promise to fetch translation from the server
|
||||
if (!translations[translationFingerprint]) {
|
||||
@ -77,15 +79,13 @@ window.getTranslation = function (translationInput) {
|
||||
fetch(`/static/translations/${translationFingerprint}`, { credentials: 'include' })
|
||||
.then(response => response.json()).then(data => ({
|
||||
language: translation,
|
||||
resources: {
|
||||
[translation]: data
|
||||
},
|
||||
data: data,
|
||||
}))
|
||||
.catch((error) => {
|
||||
delete translations[translationFingerprint];
|
||||
if (translationInput !== 'en') {
|
||||
// Couldn't load selected translation. Try a fall back to en before failing.
|
||||
return window.getTranslation('en');
|
||||
return window.getTranslation(fragment, 'en');
|
||||
}
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user