mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 15:26:36 +00:00
Core POC support for polymer i18n (#227)
* Core POC support for polymer i18n * Move translation from core.js to html * Replace fetch with XHR * Convert translation pipeline to gulp * Convert from polyglot to Polymer localize * Pass through missing keys for custom panels * Store promise to be reused * Use cacheFirst sw handler for translations * Write full filenames to translationFingerprints * Precache en translation * Convert home-assistant-main to ES6 class * Create a localization mixin * Cleanup * Add polymer tags to annotate for linter * Rename fingerprints to translationMetadata * Build translation native names into metadata * Add language selection UI to sidebar * Provide separate message namespace argument * Store language/resources on hass object * Store translationMetadata on hass * Move language selector to config panel * Temporarily hide language selector * Small cleanups * Use dynamic-align for more flexible layout * Migrate to fetch API * Only send change events for user selection events * Update for new linting rules * Migrate build_frontend changes
This commit is contained in:
parent
7b61826134
commit
29fad98754
@ -9,6 +9,7 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"app-layout": "^2.0.0",
|
||||
"app-localize-behavior": "PolymerElements/app-localize-behavior#~2.0.0",
|
||||
"app-route": "PolymerElements/app-route#^2.0.0",
|
||||
"app-storage": "^2.0.2",
|
||||
"fecha": "~2.3.0",
|
||||
|
@ -30,7 +30,7 @@ function renamePanel(path) {
|
||||
}
|
||||
}
|
||||
|
||||
gulp.task('build', ['ru_all'], () => {
|
||||
gulp.task('build', ['ru_all', 'build-translations'], () => {
|
||||
const strategy = composeStrategies([
|
||||
generateShellMergeStrategy(polymerConfig.shell),
|
||||
stripImportsStrategy([
|
||||
|
@ -39,6 +39,7 @@ var staticFingerprinted = [
|
||||
'mdi.html',
|
||||
'core.js',
|
||||
'compatibility.js',
|
||||
'translations/en.json',
|
||||
];
|
||||
|
||||
// These panels will always be registered inside HA and thus can
|
||||
@ -62,9 +63,10 @@ gulp.task('gen-service-worker', () => {
|
||||
// Create fingerprinted versions of our dependencies.
|
||||
staticFingerprinted.forEach(fn => {
|
||||
var parts = path.parse(fn);
|
||||
var hash = md5(rootDir + '/' + parts.name + parts.ext);
|
||||
var url = '/static/' + parts.name + '-' + hash + parts.ext;
|
||||
var fpath = rootDir + '/' + parts.name + parts.ext;
|
||||
var base = parts.dir.length > 0 ? parts.dir + '/' + parts.name : parts.name;
|
||||
var hash = md5(rootDir + '/' + base + parts.ext);
|
||||
var url = '/static/' + base + '-' + hash + parts.ext;
|
||||
var fpath = rootDir + '/' + base + parts.ext;
|
||||
dynamicUrlToDependencies[url] = [fpath];
|
||||
});
|
||||
|
||||
@ -89,6 +91,10 @@ gulp.task('gen-service-worker', () => {
|
||||
rootDir + '/fonts/roboto/Roboto-Bold.ttf',
|
||||
rootDir + '/images/card_media_player_bg.png',
|
||||
],
|
||||
runtimeCaching: [{
|
||||
urlPattern: /\/static\/translations\//,
|
||||
handler: 'cacheFirst',
|
||||
}],
|
||||
stripPrefix: 'hass_frontend',
|
||||
replacePrefix: 'static',
|
||||
verbose: true,
|
||||
|
111
gulp/tasks/translations.js
Executable file
111
gulp/tasks/translations.js
Executable file
@ -0,0 +1,111 @@
|
||||
const path = require('path');
|
||||
const gulp = require('gulp');
|
||||
const foreach = require('gulp-foreach');
|
||||
const hash = require('gulp-hash');
|
||||
const insert = require('gulp-insert');
|
||||
const merge = require('gulp-merge-json');
|
||||
const minify = require('gulp-jsonminify');
|
||||
const rename = require('gulp-rename');
|
||||
const transform = require('gulp-json-transform');
|
||||
|
||||
const inDir = 'translations'
|
||||
const outDir = 'build/translations';
|
||||
|
||||
const tasks = [];
|
||||
|
||||
function recursive_flatten (prefix, data) {
|
||||
var output = {};
|
||||
Object.keys(data).forEach(function (key) {
|
||||
if (typeof(data[key]) === 'object') {
|
||||
output = Object.assign({}, output, recursive_flatten(key + '.', data[key]));
|
||||
} else {
|
||||
output[prefix + key] = data[key];
|
||||
}
|
||||
});
|
||||
return output
|
||||
}
|
||||
|
||||
function flatten (data) {
|
||||
return recursive_flatten('', data);
|
||||
}
|
||||
|
||||
var taskName = 'build-translation-native-names';
|
||||
gulp.task(taskName, function() {
|
||||
return gulp.src(inDir + '/*.json')
|
||||
.pipe(transform(function(data, file) {
|
||||
// Look up the native name for each language and generate a json
|
||||
// object with all available languages and native names
|
||||
const lang = path.basename(file.relative, '.json');
|
||||
return {[lang]: {nativeName: data.language[lang]}};
|
||||
}))
|
||||
.pipe(merge({
|
||||
fileName: 'translationNativeNames.json',
|
||||
}))
|
||||
.pipe(gulp.dest('build-temp'));
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
var taskName = 'build-merged-translations';
|
||||
gulp.task(taskName, function () {
|
||||
return gulp.src(inDir + '/*.json')
|
||||
.pipe(foreach(function(stream, file) {
|
||||
// For each language generate a merged json file. It begins with en.json as
|
||||
// a failsafe for untranslated strings, and merges all parent tags into one
|
||||
// file for each specific subtag
|
||||
const tr = path.basename(file.history[0], '.json');
|
||||
const subtags = tr.split('-');
|
||||
const src = [inDir + '/en.json']; // Start with en as a fallback for missing translations
|
||||
for (i = 1; i <= subtags.length; i++) {
|
||||
const lang = subtags.slice(0, i).join('-');
|
||||
src.push(inDir + '/' + lang + '.json');
|
||||
}
|
||||
return gulp.src(src)
|
||||
.pipe(merge({
|
||||
fileName: tr + '.json',
|
||||
}))
|
||||
.pipe(transform(function(data, file) {
|
||||
// Polymer.AppLocalizeBehavior requires flattened json
|
||||
return flatten(data);
|
||||
}))
|
||||
.pipe(minify())
|
||||
.pipe(gulp.dest(outDir));
|
||||
}));
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
var taskName = 'build-translation-fingerprints';
|
||||
gulp.task(taskName, ['build-merged-translations'], function() {
|
||||
return gulp.src(outDir + '/*.json')
|
||||
.pipe(rename({
|
||||
extname: "",
|
||||
}))
|
||||
.pipe(hash({
|
||||
algorithm: 'md5',
|
||||
hashLength: 32,
|
||||
template: '<%= name %>-<%= hash %>.json',
|
||||
}))
|
||||
.pipe(hash.manifest('translationFingerprints.json'))
|
||||
.pipe(transform(function(data, file) {
|
||||
Object.keys(data).map(function(key, index) {
|
||||
data[key] = {fingerprint: data[key]};
|
||||
});
|
||||
return data;
|
||||
}))
|
||||
.pipe(gulp.dest('build-temp'));
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
var taskName = 'build-translations';
|
||||
gulp.task(taskName, ['build-translation-fingerprints', 'build-translation-native-names'], function() {
|
||||
return gulp.src([
|
||||
'build-temp/translationFingerprints.json',
|
||||
'build-temp/translationNativeNames.json',
|
||||
])
|
||||
.pipe(merge({}))
|
||||
.pipe(insert.wrap('<script>\nwindow.translationMetadata = ', ';\n</script>'))
|
||||
.pipe(rename('translationMetadata.html'))
|
||||
.pipe(gulp.dest('build-temp'));
|
||||
});
|
||||
tasks.push(taskName);
|
||||
|
||||
module.exports = tasks;
|
@ -39,8 +39,14 @@
|
||||
"gulp-babel": "^7.0.0",
|
||||
"gulp-file": "^0.3.0",
|
||||
"gulp-filter": "^5.0.1",
|
||||
"gulp-foreach": "^0.1.0",
|
||||
"gulp-hash": "^4.0.1",
|
||||
"gulp-html-minifier": "^0.1.8",
|
||||
"gulp-if": "^2.0.2",
|
||||
"gulp-insert": "^0.5.0",
|
||||
"gulp-json-transform": "^0.4.2",
|
||||
"gulp-jsonminify": "^1.0.0",
|
||||
"gulp-merge-json": "^1.0.0",
|
||||
"gulp-rename": "^1.2.2",
|
||||
"gulp-rollup-each": "^2.0.0",
|
||||
"gulp-uglify": "^3.0.0",
|
||||
|
@ -7,6 +7,7 @@
|
||||
<link rel="import" href="./ha-config-section-core.html">
|
||||
<!-- <link rel="import" href="./ha-config-section-group.html"> -->
|
||||
<link rel="import" href="./ha-config-section-hassbian.html">
|
||||
<link rel="import" href="./ha-config-section-translation.html">
|
||||
<link rel="import" href="./ha-config-section-themes.html">
|
||||
|
||||
<dom-module id="ha-config-core">
|
||||
@ -53,6 +54,14 @@
|
||||
hass='[[hass]]'
|
||||
></ha-config-section-core>
|
||||
|
||||
<template is='dom-if' if='[[computeIsTranslationLoaded(hass)]]'>
|
||||
<div class='border'></div>
|
||||
<ha-config-section-translation
|
||||
is-wide='[[isWide]]'
|
||||
hass='[[hass]]'
|
||||
></ha-config-section-translation>
|
||||
</template>
|
||||
|
||||
<template is='dom-if' if='[[computeIsThemesLoaded(hass)]]'>
|
||||
<div class='border'></div>
|
||||
<ha-config-section-themes
|
||||
@ -96,6 +105,13 @@ class HaConfigCore extends Polymer.Element {
|
||||
return window.hassUtil.isComponentLoaded(hass, 'config.zwave');
|
||||
}
|
||||
|
||||
computeIsTranslationLoaded(hass) {
|
||||
// Return false to hide language selection until i18n is ready to be
|
||||
// deployed.
|
||||
return false && hass.translationMetadata &&
|
||||
Object.keys(hass.translationMetadata).length;
|
||||
}
|
||||
|
||||
computeIsThemesLoaded(hass) {
|
||||
return hass.themes && hass.themes.themes &&
|
||||
Object.keys(hass.themes.themes).length;
|
||||
|
79
panels/config/core/ha-config-section-translation.html
Normal file
79
panels/config/core/ha-config-section-translation.html
Normal file
@ -0,0 +1,79 @@
|
||||
<link rel="import" href="../../../bower_components/polymer/polymer-element.html">
|
||||
<link rel="import" href="../../../bower_components/paper-card/paper-card.html">
|
||||
<link rel="import" href="../../../bower_components/paper-dropdown-menu/paper-dropdown-menu.html">
|
||||
<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="../ha-config-section.html">
|
||||
|
||||
<dom-module id="ha-config-section-translation">
|
||||
<template>
|
||||
<ha-config-section is-wide='[[isWide]]'>
|
||||
<span slot='header'>Choose a Language</span>
|
||||
<span slot='introduction'>
|
||||
Choose a language for the Home Assistant interface on this device.
|
||||
</span>
|
||||
|
||||
<paper-card>
|
||||
<div class='card-content'>
|
||||
<paper-dropdown-menu label="Language" dynamic-align>
|
||||
<paper-listbox slot="dropdown-content" on-selected-item-changed="handleLanguageSelect" attr-for-selected="language-tag" selected="[[language]]">
|
||||
<template is='dom-repeat' items='[[languages]]'>
|
||||
<paper-item language-tag="[[item.tag]]">[[item.nativeName]]</paper-item>
|
||||
</template>
|
||||
</paper-listbox>
|
||||
></paper-dropdown-menu>
|
||||
</div>
|
||||
</paper-card>
|
||||
</ha-config-section>
|
||||
</template>
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
/*
|
||||
* @appliesMixin window.hassMixins.LocalizeMixin
|
||||
* @appliesMixin window.hassMixins.EventsMixin
|
||||
*/
|
||||
class HaConfigSectionTranslation extends
|
||||
window.hassMixins.LocalizeMixin(window.hassMixins.EventsMixin(Polymer.Element)) {
|
||||
static get is() { return 'ha-config-section-translation'; }
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
isWide: {
|
||||
type: Boolean,
|
||||
},
|
||||
|
||||
languages: {
|
||||
type: Array,
|
||||
computed: 'computeLanguages(hass)',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
computeLanguages(hass) {
|
||||
if (!hass || !hass.translationMetadata) {
|
||||
return [];
|
||||
}
|
||||
return Object.keys(hass.translationMetadata).map(key => ({
|
||||
tag: key,
|
||||
nativeName: hass.translationMetadata[key].nativeName,
|
||||
}));
|
||||
}
|
||||
|
||||
handleLanguageSelect(ev) {
|
||||
// Only fire event if language was changed. This prevents select updates when
|
||||
// responding to hass changes.
|
||||
if (ev.detail.value && ev.detail.value.languageTag !== this.language) {
|
||||
this.fire('hass-language-select', { language: ev.detail.value.languageTag });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define(HaConfigSectionTranslation.is, HaConfigSectionTranslation);
|
||||
</script>
|
@ -21,6 +21,10 @@ cp build/*.js build/*.html $OUTPUT_DIR
|
||||
mkdir $OUTPUT_DIR/panels
|
||||
cp build/panels/*.html $OUTPUT_DIR/panels
|
||||
|
||||
# Panels
|
||||
mkdir $OUTPUT_DIR/translations
|
||||
cp build/translations/*.json $OUTPUT_DIR/translations
|
||||
|
||||
# Local Roboto
|
||||
cp -r bower_components/font-roboto-local/fonts $OUTPUT_DIR
|
||||
|
||||
@ -43,7 +47,13 @@ cp build/service_worker.js $OUTPUT_DIR
|
||||
|
||||
# GZIP frontend
|
||||
cd $OUTPUT_DIR
|
||||
gzip -f -n -k -9 *.html *.js ./panels/*.html ./fonts/roboto/*.ttf ./fonts/robotomono/*.ttf
|
||||
gzip -f -n -k -9 \
|
||||
*.html \
|
||||
*.js \
|
||||
./panels/*.html \
|
||||
./translations/*.json \
|
||||
./fonts/roboto/*.ttf \
|
||||
./fonts/robotomono/*.ttf
|
||||
cd ..
|
||||
|
||||
# Generate the __init__ file
|
||||
|
@ -9,6 +9,7 @@
|
||||
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
|
||||
|
||||
<link rel='import' href='../util/hass-mixins.html'>
|
||||
<link rel='import' href='../util/hass-translation.html'>
|
||||
|
||||
<link rel='import' href='./ha-push-notifications-toggle.html'>
|
||||
|
||||
@ -105,19 +106,19 @@
|
||||
<paper-listbox attr-for-selected='data-panel' selected='[[route.panel]]'>
|
||||
<paper-icon-item on-tap='menuClicked' data-panel='states'>
|
||||
<iron-icon slot="item-icon" icon='mdi:apps'></iron-icon>
|
||||
<span class='item-text'>States</span>
|
||||
<span class='item-text'>{{localize('panel', 'states')}}</span>
|
||||
</paper-icon-item>
|
||||
|
||||
<template is='dom-repeat' items='[[panels]]'>
|
||||
<paper-icon-item on-tap='menuClicked' data-panel$='[[item.url_path]]'>
|
||||
<iron-icon slot="item-icon" icon='[[item.icon]]'></iron-icon>
|
||||
<span class='item-text'>[[item.title]]</span>
|
||||
<span class='item-text'>{{localize('panel', item.title)}}</span>
|
||||
</paper-icon-item>
|
||||
</template>
|
||||
|
||||
<paper-icon-item on-tap='menuClicked' data-panel='logout' class='logout'>
|
||||
<iron-icon slot="item-icon" icon='mdi:exit-to-app'></iron-icon>
|
||||
<span class='item-text'>Log Out</span>
|
||||
<span class='item-text'>{{localize('panel', 'log_out')}}</span>
|
||||
</paper-icon-item>
|
||||
</paper-listbox>
|
||||
|
||||
@ -171,7 +172,12 @@
|
||||
</dom-module>
|
||||
|
||||
<script>
|
||||
class HaSidebar extends window.hassMixins.EventsMixin(Polymer.Element) {
|
||||
/*
|
||||
* @appliesMixin window.hassMixins.LocalizeMixin
|
||||
* @appliesMixin window.hassMixins.EventsMixin
|
||||
*/
|
||||
class HaSidebar extends
|
||||
window.hassMixins.LocalizeMixin(window.hassMixins.EventsMixin(Polymer.Element)) {
|
||||
static get is() { return 'ha-sidebar'; }
|
||||
|
||||
static get properties() {
|
||||
|
@ -1,4 +1,5 @@
|
||||
<link rel="import" href='../bower_components/polymer/polymer-element.html'>
|
||||
<link rel='import' href='../bower_components/app-localize-behavior/app-localize-behavior.html'>
|
||||
<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'>
|
||||
@ -7,6 +8,7 @@
|
||||
<link rel="import" href="../bower_components/neon-animation/web-animations.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'>
|
||||
<link rel='import' href='./util/hass-call-api.html'>
|
||||
@ -84,7 +86,9 @@ class HomeAssistant extends Polymer.Element {
|
||||
ready() {
|
||||
super.ready();
|
||||
this.addEventListener('settheme', e => this.setTheme(e));
|
||||
this.addEventListener('hass-language-select', e => this.selectLanguage(e));
|
||||
this.loadIcons();
|
||||
this.loadResources();
|
||||
}
|
||||
|
||||
computeShowMain(hass, iconsLoaded) {
|
||||
@ -112,6 +116,15 @@ class HomeAssistant extends Polymer.Element {
|
||||
);
|
||||
}
|
||||
|
||||
loadResources() {
|
||||
window.getTranslation().then((result) => {
|
||||
this._updateHass({
|
||||
language: result.language,
|
||||
resources: result.resources,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
connectionChanged(conn, oldConn) {
|
||||
if (oldConn) {
|
||||
this.unsubConnection();
|
||||
@ -128,6 +141,9 @@ class HomeAssistant extends Polymer.Element {
|
||||
states: null,
|
||||
config: null,
|
||||
themes: null,
|
||||
language: null,
|
||||
resources: null,
|
||||
translationMetadata: window.translationMetadata,
|
||||
dockedSidebar: false,
|
||||
moreInfoEntityId: null,
|
||||
callService: (domain, service, serviceData) =>
|
||||
@ -256,6 +272,12 @@ class HomeAssistant extends Polymer.Element {
|
||||
this.$.storage.storeState();
|
||||
}
|
||||
|
||||
selectLanguage(event) {
|
||||
this._updateHass({ selectedLanguage: event.detail.language });
|
||||
this.$.storage.storeState();
|
||||
this.loadResources();
|
||||
}
|
||||
|
||||
_updateHass(obj) {
|
||||
this.hass = Object.assign({}, this.hass, obj);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
var STORED_STATE = [
|
||||
'dockedSidebar',
|
||||
'selectedTheme',
|
||||
'selectedLanguage',
|
||||
];
|
||||
|
||||
Polymer({
|
||||
|
@ -77,4 +77,37 @@ window.hassMixins.NavigateMixin = Polymer.dedupingMixin(superClass =>
|
||||
}
|
||||
});
|
||||
|
||||
/* @polymerMixin */
|
||||
window.hassMixins.LocalizeMixin = Polymer.dedupingMixin(superClass =>
|
||||
class extends Polymer.mixinBehaviors([Polymer.AppLocalizeBehavior], superClass) {
|
||||
static get properties() {
|
||||
return {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
language: {
|
||||
type: String,
|
||||
computed: 'computeLanguage(hass)',
|
||||
},
|
||||
resources: {
|
||||
type: Object,
|
||||
computed: 'computeResources(hass)',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
computeLanguage(hass) {
|
||||
return hass && hass.language;
|
||||
}
|
||||
|
||||
computeResources(hass) {
|
||||
return hass && hass.resources;
|
||||
}
|
||||
|
||||
localize(namespace, message, ...args) {
|
||||
// Return the input message if no translation is found
|
||||
return super.localize(namespace + '.' + message, ...args) || message;
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
84
src/util/hass-translation.html
Normal file
84
src/util/hass-translation.html
Normal file
@ -0,0 +1,84 @@
|
||||
<link rel='import' href='../../build-temp/translationMetadata.html' />
|
||||
|
||||
<script>
|
||||
function getActiveTranslation() {
|
||||
// Perform case-insenstive comparison since browser isn't required to
|
||||
// report languages with specific cases.
|
||||
const lookup = {};
|
||||
/* eslint-disable no-undef */
|
||||
Object.keys(window.translationMetadata).forEach(function (tr) {
|
||||
lookup[tr.toLowerCase()] = tr;
|
||||
});
|
||||
|
||||
// Search for a matching translation from most specific to general
|
||||
function languageGetTranslation(language) {
|
||||
const subtags = language.toLowerCase().split('-');
|
||||
|
||||
for (var i = subtags.length; i >= 1; i--) {
|
||||
const lang = subtags.slice(0, i).join('-');
|
||||
|
||||
if (lookup[lang]) {
|
||||
return lookup[lang];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
var translation = null;
|
||||
if (window.localStorage.selectedLanguage) {
|
||||
translation = languageGetTranslation(JSON.parse(window.localStorage.selectedLanguage));
|
||||
if (translation) {
|
||||
return translation;
|
||||
}
|
||||
} else if (navigator.languages) {
|
||||
for (var i = 0; i < navigator.languages.length; i++) {
|
||||
translation = languageGetTranslation(navigator.languages[i]);
|
||||
if (translation) {
|
||||
return translation;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
translation = languageGetTranslation(navigator.language || navigator.userLanguage);
|
||||
if (translation) {
|
||||
return translation;
|
||||
}
|
||||
}
|
||||
|
||||
// Final fallback
|
||||
return 'en';
|
||||
}
|
||||
|
||||
// Store loaded translations in memory so translations are available immediately
|
||||
// when DOM is created in Polymer. Even a cache lookup creates noticable latency.
|
||||
const translations = {};
|
||||
|
||||
window.getTranslation = function (translationInput) {
|
||||
const translation = translationInput || getActiveTranslation();
|
||||
const translationFingerprint = window.translationMetadata[translation].fingerprint;
|
||||
|
||||
// Create a promise to fetch translation from the server
|
||||
if (!translations[translationFingerprint]) {
|
||||
translations[translationFingerprint] =
|
||||
fetch('/static/translations/' + translationFingerprint)
|
||||
.then(response => response.json()).then(data => ({
|
||||
language: translation,
|
||||
resources: {
|
||||
[translation]: 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 Promise.reject(error);
|
||||
});
|
||||
}
|
||||
return translations[translationFingerprint];
|
||||
};
|
||||
|
||||
// Load selected translation into memory immediately so it is ready when Polymer
|
||||
// initializes.
|
||||
window.getTranslation();
|
||||
</script>
|
12
translations/en.json
Normal file
12
translations/en.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"language": {
|
||||
"en": "English"
|
||||
},
|
||||
"panel": {
|
||||
"states": "States",
|
||||
"map": "Map",
|
||||
"logbook": "Logbook",
|
||||
"history": "History",
|
||||
"log_out": "Log Out"
|
||||
}
|
||||
}
|
164
yarn.lock
164
yarn.lock
@ -2378,6 +2378,10 @@ depd@~1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.0.1.tgz#80aec64c9d6d97e65cc2a9caa93c0aa6abf73aaa"
|
||||
|
||||
deprecate@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/deprecate/-/deprecate-1.0.0.tgz#661490ed2428916a6c8883d8834e5646f4e4a4a8"
|
||||
|
||||
deprecated@^0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/deprecated/-/deprecated-0.0.1.tgz#f9c9af5464afa1e7a971458a8bdef2aa94d5bb19"
|
||||
@ -2673,7 +2677,7 @@ es6-object-assign@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
|
||||
|
||||
es6-promise@^2.1.0:
|
||||
es6-promise@^2.1.0, es6-promise@^2.1.1:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-2.3.0.tgz#96edb9f2fdb01995822b263dd8aadab6748181bc"
|
||||
|
||||
@ -3646,6 +3650,23 @@ gulp-filter@^5.0.1:
|
||||
multimatch "^2.0.0"
|
||||
streamfilter "^1.0.5"
|
||||
|
||||
gulp-foreach@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/gulp-foreach/-/gulp-foreach-0.1.0.tgz#2547abfd1a1b75f569b54194190ef1c0b0289538"
|
||||
dependencies:
|
||||
gulp-util "~2.2.14"
|
||||
through2 "~0.6.3"
|
||||
|
||||
gulp-hash@^4.0.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/gulp-hash/-/gulp-hash-4.1.1.tgz#7779910f6c378686b62f88abbb57ebeef20945d0"
|
||||
dependencies:
|
||||
es6-promise "^2.1.1"
|
||||
gulp-util "^2.2.17"
|
||||
lodash.assign "^2.4.1"
|
||||
lodash.template "^2.4.1"
|
||||
through2 "^2.0.0"
|
||||
|
||||
gulp-html-minifier@^0.1.8:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/gulp-html-minifier/-/gulp-html-minifier-0.1.8.tgz#b0dd99d96ad01c0a88d79465ed44f94d6a2b549a"
|
||||
@ -3663,12 +3684,45 @@ gulp-if@^2.0.0, gulp-if@^2.0.2:
|
||||
ternary-stream "^2.0.1"
|
||||
through2 "^2.0.1"
|
||||
|
||||
gulp-insert@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/gulp-insert/-/gulp-insert-0.5.0.tgz#32313f13e4a23cf5acca5ce5f0c080923c778602"
|
||||
dependencies:
|
||||
readable-stream "^1.0.26-4"
|
||||
streamqueue "0.0.6"
|
||||
|
||||
gulp-json-transform@^0.4.2:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/gulp-json-transform/-/gulp-json-transform-0.4.2.tgz#2245ed252fa309c97b8bdd9d1a19cdd149e32685"
|
||||
dependencies:
|
||||
gulp-util "^3.0.7"
|
||||
promise "^7.1.1"
|
||||
through2 "^2.0.1"
|
||||
|
||||
gulp-jsonminify@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/gulp-jsonminify/-/gulp-jsonminify-1.0.0.tgz#c42b68812c657e6e0a12e93c2aa217b3eb38c70a"
|
||||
dependencies:
|
||||
gulp-util "~3.0.4"
|
||||
jsonminify "~0.2.3"
|
||||
through2 "~0.6.5"
|
||||
|
||||
gulp-match@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/gulp-match/-/gulp-match-1.0.3.tgz#91c7c0d7f29becd6606d57d80a7f8776a87aba8e"
|
||||
dependencies:
|
||||
minimatch "^3.0.3"
|
||||
|
||||
gulp-merge-json@^1.0.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/gulp-merge-json/-/gulp-merge-json-1.2.0.tgz#8b68f297505278a8143a6fa5bb9830192e4e3ea1"
|
||||
dependencies:
|
||||
deprecate "^1.0.0"
|
||||
gulp-util "^3.0.8"
|
||||
json5 "^0.5.1"
|
||||
lodash "^4.17.4"
|
||||
through "^2.3.8"
|
||||
|
||||
gulp-rename@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/gulp-rename/-/gulp-rename-1.2.2.tgz#3ad4428763f05e2764dec1c67d868db275687817"
|
||||
@ -3703,7 +3757,7 @@ gulp-uglify@^3.0.0:
|
||||
uglify-js "^3.0.5"
|
||||
vinyl-sourcemaps-apply "^0.2.0"
|
||||
|
||||
gulp-util@^2.2.14, gulp-util@^2.2.20:
|
||||
gulp-util@^2.2.14, gulp-util@^2.2.17, gulp-util@^2.2.20, gulp-util@~2.2.14:
|
||||
version "2.2.20"
|
||||
resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-2.2.20.tgz#d7146e5728910bd8f047a6b0b1e549bc22dbd64c"
|
||||
dependencies:
|
||||
@ -3716,7 +3770,7 @@ gulp-util@^2.2.14, gulp-util@^2.2.20:
|
||||
through2 "^0.5.0"
|
||||
vinyl "^0.2.1"
|
||||
|
||||
gulp-util@^3.0.0, gulp-util@^3.0.6, gulp-util@^3.0.8:
|
||||
gulp-util@^3.0.0, gulp-util@^3.0.6, gulp-util@^3.0.7, gulp-util@^3.0.8, gulp-util@~3.0.4:
|
||||
version "3.0.8"
|
||||
resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f"
|
||||
dependencies:
|
||||
@ -4488,6 +4542,10 @@ jsonify@~0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
|
||||
|
||||
jsonminify@~0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/jsonminify/-/jsonminify-0.2.3.tgz#4b842c8a3fe5d6aa48b2f8f95a1cf9a80c019d8e"
|
||||
|
||||
jsonpointer@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
|
||||
@ -4608,6 +4666,15 @@ lodash._baseassign@^3.0.0:
|
||||
lodash._basecopy "^3.0.0"
|
||||
lodash.keys "^3.0.0"
|
||||
|
||||
lodash._basebind@~2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._basebind/-/lodash._basebind-2.4.1.tgz#e940b9ebdd27c327e0a8dab1b55916c5341e9575"
|
||||
dependencies:
|
||||
lodash._basecreate "~2.4.1"
|
||||
lodash._setbinddata "~2.4.1"
|
||||
lodash._slice "~2.4.1"
|
||||
lodash.isobject "~2.4.1"
|
||||
|
||||
lodash._basecopy@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36"
|
||||
@ -4616,6 +4683,32 @@ lodash._basecreate@^3.0.0:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821"
|
||||
|
||||
lodash._basecreate@~2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-2.4.1.tgz#f8e6f5b578a9e34e541179b56b8eeebf4a287e08"
|
||||
dependencies:
|
||||
lodash._isnative "~2.4.1"
|
||||
lodash.isobject "~2.4.1"
|
||||
lodash.noop "~2.4.1"
|
||||
|
||||
lodash._basecreatecallback@~2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._basecreatecallback/-/lodash._basecreatecallback-2.4.1.tgz#7d0b267649cb29e7a139d0103b7c11fae84e4851"
|
||||
dependencies:
|
||||
lodash._setbinddata "~2.4.1"
|
||||
lodash.bind "~2.4.1"
|
||||
lodash.identity "~2.4.1"
|
||||
lodash.support "~2.4.1"
|
||||
|
||||
lodash._basecreatewrapper@~2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._basecreatewrapper/-/lodash._basecreatewrapper-2.4.1.tgz#4d31f2e7de7e134fbf2803762b8150b32519666f"
|
||||
dependencies:
|
||||
lodash._basecreate "~2.4.1"
|
||||
lodash._setbinddata "~2.4.1"
|
||||
lodash._slice "~2.4.1"
|
||||
lodash.isobject "~2.4.1"
|
||||
|
||||
lodash._basetostring@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5"
|
||||
@ -4624,6 +4717,15 @@ lodash._basevalues@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7"
|
||||
|
||||
lodash._createwrapper@~2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._createwrapper/-/lodash._createwrapper-2.4.1.tgz#51d6957973da4ed556e37290d8c1a18c53de1607"
|
||||
dependencies:
|
||||
lodash._basebind "~2.4.1"
|
||||
lodash._basecreatewrapper "~2.4.1"
|
||||
lodash._slice "~2.4.1"
|
||||
lodash.isfunction "~2.4.1"
|
||||
|
||||
lodash._escapehtmlchar@~2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz#df67c3bb6b7e8e1e831ab48bfa0795b92afe899d"
|
||||
@ -4681,12 +4783,38 @@ lodash._root@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692"
|
||||
|
||||
lodash._setbinddata@~2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._setbinddata/-/lodash._setbinddata-2.4.1.tgz#f7c200cd1b92ef236b399eecf73c648d17aa94d2"
|
||||
dependencies:
|
||||
lodash._isnative "~2.4.1"
|
||||
lodash.noop "~2.4.1"
|
||||
|
||||
lodash._shimkeys@~2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz#6e9cc9666ff081f0b5a6c978b83e242e6949d203"
|
||||
dependencies:
|
||||
lodash._objecttypes "~2.4.1"
|
||||
|
||||
lodash._slice@~2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._slice/-/lodash._slice-2.4.1.tgz#745cf41a53597b18f688898544405efa2b06d90f"
|
||||
|
||||
lodash.assign@^2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-2.4.1.tgz#84c39596dd71181a97b0652913a7c9675e49b1aa"
|
||||
dependencies:
|
||||
lodash._basecreatecallback "~2.4.1"
|
||||
lodash._objecttypes "~2.4.1"
|
||||
lodash.keys "~2.4.1"
|
||||
|
||||
lodash.bind@~2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-2.4.1.tgz#5d19fa005c8c4d236faf4742c7b7a1fcabe29267"
|
||||
dependencies:
|
||||
lodash._createwrapper "~2.4.1"
|
||||
lodash._slice "~2.4.1"
|
||||
|
||||
lodash.cond@^4.3.0:
|
||||
version "4.5.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5"
|
||||
@ -4724,6 +4852,10 @@ lodash.escape@~2.4.1:
|
||||
lodash._reunescapedhtml "~2.4.1"
|
||||
lodash.keys "~2.4.1"
|
||||
|
||||
lodash.identity@~2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.identity/-/lodash.identity-2.4.1.tgz#6694cffa65fef931f7c31ce86c74597cf560f4f1"
|
||||
|
||||
lodash.isarguments@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
|
||||
@ -4736,6 +4868,10 @@ lodash.isequal@^4.0.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||
|
||||
lodash.isfunction@~2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-2.4.1.tgz#2cfd575c73e498ab57e319b77fa02adef13a94d1"
|
||||
|
||||
lodash.isobject@~2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-2.4.1.tgz#5a2e47fe69953f1ee631a7eba1fe64d2d06558f5"
|
||||
@ -4770,6 +4906,10 @@ lodash.mapvalues@^4.4.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c"
|
||||
|
||||
lodash.noop@~2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-2.4.1.tgz#4fb54f816652e5ae10e8f72f717a388c7326538a"
|
||||
|
||||
lodash.restparam@^3.0.0:
|
||||
version "3.6.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
|
||||
@ -4778,6 +4918,12 @@ lodash.some@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
|
||||
|
||||
lodash.support@~2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.support/-/lodash.support-2.4.1.tgz#320e0b67031673c28d7a2bb5d9e0331a45240515"
|
||||
dependencies:
|
||||
lodash._isnative "~2.4.1"
|
||||
|
||||
lodash.template@^2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-2.4.1.tgz#9e611007edf629129a974ab3c48b817b3e1cf20d"
|
||||
@ -6139,7 +6285,7 @@ read-pkg@^2.0.0:
|
||||
normalize-package-data "^2.3.2"
|
||||
path-type "^2.0.0"
|
||||
|
||||
readable-stream@1.1.x, readable-stream@~1.1.9:
|
||||
readable-stream@1.1.x, readable-stream@^1.0.26-2, readable-stream@^1.0.26-4, readable-stream@~1.1.9:
|
||||
version "1.1.14"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
|
||||
dependencies:
|
||||
@ -6979,6 +7125,12 @@ streamfilter@^1.0.5:
|
||||
dependencies:
|
||||
readable-stream "^2.0.2"
|
||||
|
||||
streamqueue@0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/streamqueue/-/streamqueue-0.0.6.tgz#66f5f5ec94e9b8af249e4aec2dd1f741bfe94de3"
|
||||
dependencies:
|
||||
readable-stream "^1.0.26-2"
|
||||
|
||||
streamsearch@0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a"
|
||||
@ -7266,7 +7418,7 @@ through2@^0.5.0:
|
||||
readable-stream "~1.0.17"
|
||||
xtend "~3.0.0"
|
||||
|
||||
through2@^0.6.0, through2@^0.6.1:
|
||||
through2@^0.6.0, through2@^0.6.1, through2@~0.6.3, through2@~0.6.5:
|
||||
version "0.6.5"
|
||||
resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48"
|
||||
dependencies:
|
||||
@ -7280,7 +7432,7 @@ through2@^2.0.0, through2@^2.0.1, through2@^2.0.3, through2@~2.0.0:
|
||||
readable-stream "^2.1.5"
|
||||
xtend "~4.0.1"
|
||||
|
||||
through@2, through@^2.3.6, through@~2.3, through@~2.3.1:
|
||||
through@2, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1:
|
||||
version "2.3.8"
|
||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user