From 9af8eedb8ad576fcd92a789403d8642207e855b4 Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Sun, 9 Oct 2022 12:37:24 -0700 Subject: [PATCH] Dynamically load non-fallback languages. --- rollup.config.js | 12 +++++++++++- src/card.ts | 21 ++++++++++++++++----- src/localize/localize.ts | 36 ++++++++++++++++++++++++++++-------- 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index 13c20382..ea5e9be6 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -8,7 +8,7 @@ import styles from 'rollup-plugin-styles'; import image from '@rollup/plugin-image'; import replace from '@rollup/plugin-replace'; import gitInfo from 'rollup-plugin-git-info'; -import { visualizer } from "rollup-plugin-visualizer"; +import { visualizer } from 'rollup-plugin-visualizer'; const watch = process.env.ROLLUP_WATCH === 'true' || process.env.ROLLUP_WATCH === '1'; const dev = watch || process.env.DEV === 'true' || process.env.DEV === '1'; @@ -69,6 +69,16 @@ const config = { output: { entryFileNames: 'frigate-hass-card.js', dir: 'dist', + chunkFileNames: (chunk) => { + // Add "lang-" to the front of the language chunk names for readability. + if ( + chunk.facadeModuleId && + chunk.facadeModuleId.match(/localize\/languages\/.*\.json/) + ) { + return 'lang-[name]-[hash].js'; + } + return '[name]-[hash].js'; + }, format: 'es', ...(dev && { sourcemap: true, diff --git a/src/card.ts b/src/card.ts index 0f0c02fb..d8da19c4 100644 --- a/src/card.ts +++ b/src/card.ts @@ -37,7 +37,7 @@ import { MEDIA_PLAYER_SUPPORT_BROWSE_MEDIA, REPO_URL, } from './const.js'; -import { getLanguage, localize } from './localize/localize.js'; +import { getLanguage, loadLanguages, localize } from './localize/localize.js'; import cardStyle from './scss/card.scss'; import { Actions, @@ -206,7 +206,8 @@ export class FrigateCard extends LitElement { protected _boundMouseHandler = throttle(this._mouseHandler.bind(this), 1 * 1000); // Whether the card has been successfully initialized. - protected _initialized = false; + protected _loadedHAElements = false; + protected _loadedLanguages = false; protected _triggers: Map = new Map(); protected _untriggerTimerID: number | null = null; @@ -1101,11 +1102,12 @@ export class FrigateCard extends LitElement { * Called before each update. */ protected willUpdate(changedProps: PropertyValues): void { - // Side load the necessary elements if not already initialized. - if (!this._initialized) { + // Side load the necessary elements if not already initialized (do not need + // to block for the loading to complete). + if (!this._loadedHAElements) { sideLoadHomeAssistantElements().then((success) => { if (success) { - this._initialized = true; + this._loadedHAElements = true; } }); } @@ -1244,6 +1246,15 @@ export class FrigateCard extends LitElement { * @returns `true` if the element should be updated. */ protected shouldUpdate(changedProps: PropertyValues): boolean { + // Load the relevant languages. Cannot do anything until then. + if (!this._loadedLanguages) { + loadLanguages().then(() => { + this._loadedLanguages = true; + this.requestUpdate(); + }); + return false; + } + const oldHass = changedProps.get('_hass') as HomeAssistant | undefined; let shouldUpdate = !oldHass || changedProps.size != 1; diff --git a/src/localize/localize.ts b/src/localize/localize.ts index cf174c5e..e74cff88 100644 --- a/src/localize/localize.ts +++ b/src/localize/localize.ts @@ -1,14 +1,16 @@ import * as en from './languages/en.json'; -import * as pt_BR from './languages/pt-BR.json'; -import * as it from './languages/it.json'; // eslint-disable-next-line @typescript-eslint/no-explicit-any const languages: Record = { + // English as always loaded as it's the fallback language that will be used + // when translations are not found or before they are loaded (via + // loadLanguages()). en: en, - pt_BR: pt_BR, - it: it, -}; +} +/** + * Get the configured language. + */ export function getLanguage(): string { const canonicalizeLanguage = (language?: string | null): string | null => { if (!language) { @@ -39,14 +41,32 @@ export function getLanguage(): string { return lang || 'en'; } +/** + * Load required languages. + */ +export const loadLanguages = async (): Promise => { + const lang = getLanguage(); + if (lang === 'it') { + languages['it'] = await import('./languages/it.json'); + } else if (lang === 'pt_BR') { + languages['pt_BR'] = await import('./languages/pt-BR.json'); + } +} + +/** + * Get a localized version of a given string key. + * @param string The key. + * @param search An optional search key to be used with 'replace'. + * @param replace An optional replacement text to be used with 'search'. + * @returns + */ export function localize(string: string, search = '', replace = ''): string { const lang = getLanguage(); - let translated: string; + let translated = ''; try { translated = string.split('.').reduce((o, i) => o[i], languages[lang]); - } catch (e) { - translated = string.split('.').reduce((o, i) => o[i], languages['en']); + } catch (_) { } if (!translated) {