From b72a3361c076a5399bfd5a10c91987911899a80a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 28 Feb 2020 23:01:45 +0100 Subject: [PATCH 01/36] Fixes for brightness automation action (#5003) * Fixes for brightness * Add checbox before optional range integer * Console * Comments --- src/components/ha-form/ha-form-integer.ts | 53 ++++++++++++++----- src/components/ha-form/ha-form.ts | 2 +- src/components/ha-paper-slider.js | 5 ++ src/data/device_automation.ts | 2 +- .../types/ha-automation-action-device_id.ts | 25 +++++---- 5 files changed, 63 insertions(+), 24 deletions(-) diff --git a/src/components/ha-form/ha-form-integer.ts b/src/components/ha-form/ha-form-integer.ts index f4877386e0..155132c28c 100644 --- a/src/components/ha-form/ha-form-integer.ts +++ b/src/components/ha-form/ha-form-integer.ts @@ -5,6 +5,8 @@ import { property, TemplateResult, query, + CSSResult, + css, } from "lit-element"; import { HaFormElement, @@ -19,13 +21,14 @@ import "@polymer/paper-input/paper-input"; // tslint:disable-next-line import { PaperInputElement } from "@polymer/paper-input/paper-input"; import { PaperSliderElement } from "@polymer/paper-slider/paper-slider"; +import { HaCheckbox } from "../ha-checkbox"; @customElement("ha-form-integer") export class HaFormInteger extends LitElement implements HaFormElement { @property() public schema!: HaFormIntegerSchema; - @property() public data!: HaFormIntegerData; - @property() public label!: string; - @property() public suffix!: string; + @property() public data?: HaFormIntegerData; + @property() public label?: string; + @property() public suffix?: string; @query("paper-input ha-paper-slider") private _input?: HTMLElement; public focus() { @@ -39,20 +42,31 @@ export class HaFormInteger extends LitElement implements HaFormElement { ? html`
${this.label} - +
+ ${this.schema.optional && this.schema.default === undefined + ? html` + + ` + : ""} + +
` : html` .slider-knob > .slider-knob-inner { + background-color: var(--paper-slider-disabled-knob-color, var(--paper-grey-400)); + border: 2px solid var(--paper-slider-disabled-knob-color, var(--paper-grey-400)); + } + .pin > .slider-knob > .slider-knob-inner::before { top: unset; margin-left: unset; diff --git a/src/data/device_automation.ts b/src/data/device_automation.ts index f23222f2ac..61b87d8852 100644 --- a/src/data/device_automation.ts +++ b/src/data/device_automation.ts @@ -66,7 +66,7 @@ export const fetchDeviceTriggerCapabilities = ( trigger, }); -const whitelist = ["above", "below", "code", "for"]; +const whitelist = ["above", "below", "brightness", "code", "for"]; export const deviceAutomationsEqual = ( a: DeviceAutomation, diff --git a/src/panels/config/automation/action/types/ha-automation-action-device_id.ts b/src/panels/config/automation/action/types/ha-automation-action-device_id.ts index 003ddd55c0..a3b0290f84 100644 --- a/src/panels/config/automation/action/types/ha-automation-action-device_id.ts +++ b/src/panels/config/automation/action/types/ha-automation-action-device_id.ts @@ -10,6 +10,7 @@ import { import { LitElement, customElement, property, html } from "lit-element"; import { fireEvent } from "../../../../../common/dom/fire_event"; import { HomeAssistant } from "../../../../../types"; +import memoizeOne from "memoize-one"; @customElement("ha-automation-action-device_id") export class HaDeviceAction extends LitElement { @@ -27,14 +28,20 @@ export class HaDeviceAction extends LitElement { }; } + private _extraFieldsData = memoizeOne((capabilities, action: DeviceAction) => + capabilities && capabilities.extra_fields + ? capabilities.extra_fields.map((item) => { + return { [item.name]: action[item.name] }; + }) + : undefined + ); + protected render() { const deviceId = this._deviceId || this.action.device_id; - const extraFieldsData = - this._capabilities && this._capabilities.extra_fields - ? this._capabilities.extra_fields.map((item) => { - return { [item.name]: this.action[item.name] }; - }) - : undefined; + const extraFieldsData = this._extraFieldsData( + this._capabilities, + this.action + ); return html` Date: Sat, 29 Feb 2020 00:32:31 +0000 Subject: [PATCH 02/36] [ci skip] Translation update --- translations/de.json | 55 +++++++++++++++++++ translations/en.json | 77 +++++++++++++++++++++++++- translations/lb.json | 65 ++++++++++++++++++++-- translations/nb.json | 4 +- translations/pl.json | 67 +++++++++++++++++++++- translations/zh-Hans.json | 113 +++++++++++++++++++++++++++++--------- translations/zh-Hant.json | 2 +- 7 files changed, 348 insertions(+), 35 deletions(-) diff --git a/translations/de.json b/translations/de.json index 871ed3ce52..01197c5afa 100644 --- a/translations/de.json +++ b/translations/de.json @@ -640,6 +640,39 @@ "default_confirmation_title": "Sind Sie sicher?", "ok": "OK" }, + "helper_settings": { + "generic": { + "icon": "Symbol", + "initial_value": "Anfangswert beim Start", + "name": "Name" + }, + "input_datetime": { + "has_date": "Datum", + "has_time": "Zeit" + }, + "input_number": { + "max": "Maximaler Wert", + "min": "Minimaler Wert", + "mode": "Anzeigemodus", + "slider": "Schieberegler", + "unit_of_measurement": "Maßeinheit" + }, + "input_select": { + "add": "Hinzufügen", + "add_option": "Option hinzufügen", + "no_options": "Es gibt noch keine Optionen.", + "options": "Optionen" + }, + "input_text": { + "max": "Maximale Länge", + "min": "Minimale Länge", + "mode": "Anzeigemodus", + "password": "Passwort", + "text": "Text" + }, + "not_editable": "Nicht bearbeitbar", + "required_error_msg": "Dieses Feld ist erforderlich" + }, "more_info_control": { "dismiss": "Dialog ausblenden", "edit": "Entität bearbeiten", @@ -1356,6 +1389,28 @@ } }, "header": "Home Assistant konfigurieren", + "helpers": { + "caption": "Helfer", + "dialog": { + "add_helper": "Helfer hinzufügen", + "add_platform": "{platform} hinzufügen", + "create": "Erstellen" + }, + "picker": { + "add_helper": "Helfer hinzufügen", + "headers": { + "editable": "Editierbar", + "name": "Name", + "type": "Typ" + } + }, + "types": { + "input_boolean": "Umschalten", + "input_datetime": "Datum und/oder Uhrzeit", + "input_number": "Nummer", + "input_text": "Text" + } + }, "integrations": { "caption": "Integrationen", "config_entry": { diff --git a/translations/en.json b/translations/en.json index ef333b5aaf..c2c77b2f3e 100644 --- a/translations/en.json +++ b/translations/en.json @@ -639,6 +639,7 @@ }, "generic": { "cancel": "Cancel", + "close": "close", "default_confirmation_title": "Are you sure?", "ok": "OK" }, @@ -1481,6 +1482,76 @@ "note_about_website_reference": "More are available on the " }, "introduction": "Here it is possible to configure your components and Home Assistant. Not everything is possible to configure from the UI yet, but we're working on it.", + "lovelace": { + "caption": "Lovelace Dashboards", + "dashboards": { + "cant_edit_yaml": "Dashboards defined in YAML can not be edited from the UI. Change them in configuration.yaml.", + "caption": "Dashboards", + "conf_mode": { + "storage": "UI controlled", + "yaml": "YAML file" + }, + "confirm_delete": "Are you sure you want to delete this dashboard?", + "detail": { + "create": "Create", + "delete": "Delete", + "dismiss": "Close", + "edit_dashboard": "Edit dashboard", + "icon": "Sidebar icon", + "new_dashboard": "Add new dashboard", + "require_admin": "Admin only", + "show_sidebar": "Show in sidebar", + "title": "Sidebar title", + "update": "Update", + "url": "Url", + "url_error_msg": "The url can not contain spaces or special characters, except for _ and -" + }, + "picker": { + "add_dashboard": "Add dashboard", + "headers": { + "conf_mode": "Configuration method", + "filename": "Filename", + "require_admin": "Admin only", + "sidebar": "Show in sidebar", + "title": "Title" + }, + "open": "Open dashboard" + } + }, + "description": "Configure your Lovelace Dashboards", + "resources": { + "cant_edit_yaml": "You are using Lovelace in YAML mode, therefore you can not manage your resources through the UI. Manage them in configuration.yaml.", + "caption": "Resources", + "confirm_delete": "Are you sure you want to delete this resource?", + "detail": { + "create": "Create", + "delete": "Delete", + "dismiss": "Close", + "new_resource": "Add new resource", + "type": "Resource type", + "update": "Update", + "url": "Url", + "url_error_msg": "Url is a required field", + "warning_header": "Be cautious!", + "warning_text": "Adding resources can be dangerous, make sure you know the source of the resource and trust them. Bad resources could seriously harm your system." + }, + "picker": { + "add_resource": "Add resource", + "headers": { + "type": "Type", + "url": "Url" + } + }, + "refresh_body": "You have to refresh the page to complete the removal, do you want to refresh now?", + "refresh_header": "Do you want to refresh?", + "types": { + "css": "Stylesheet", + "html": "HTML (deprecated)", + "js": "JavaScript File (deprecated)", + "module": "JavaScript Module" + } + } + }, "person": { "add_person": "Add Person", "caption": "Persons", @@ -2180,10 +2251,14 @@ }, "save_config": { "cancel": "Never mind", + "close": "Close", "header": "Take control of your Lovelace UI", "para": "By default Home Assistant will maintain your user interface, updating it when new entities or Lovelace UI components become available. If you take control we will no longer make changes automatically for you.", "para_sure": "Are you sure you want to take control of your user interface?", - "save": "Take control" + "save": "Take control", + "yaml_config": "To help you start here is the current config of this dashboard:", + "yaml_control": "To take control in YAML mode, create a YAML file with the name you specified in your config for this dashboard, or the default 'ui-lovelace.yaml'.", + "yaml_mode": "You are using YAML mode, that means you can not change your Lovelace config from the UI. If you want to change Lovelace from the UI, remove the 'mode: yaml' from your Lovelace configuration in 'configuration.yaml.'" }, "suggest_card": { "add": "Add to Lovelace UI", diff --git a/translations/lb.json b/translations/lb.json index 9341113074..ff5b08711c 100644 --- a/translations/lb.json +++ b/translations/lb.json @@ -626,6 +626,7 @@ "enabled_description": "Deaktivéiert Entitéiten ginn net am Home Assistant bäigesat.", "enabled_label": "Entitéit aktivéieren", "entity_id": "ID vun der Entitéit", + "icon": "Ikon iwwerschreiwen", "name": "Numm iwwerschreiwen", "note": "Nott: dëst funktionéiert villäicht nach net mat all Integratioun.", "unavailable": "Dës Entitéit ass net erreechbar fir de Moment.", @@ -640,6 +641,38 @@ "default_confirmation_title": "Sécher?", "ok": "OK" }, + "helper_settings": { + "generic": { + "icon": "Ikon", + "initial_value": "Initial Wäert beim start", + "name": "Numm" + }, + "input_datetime": { + "has_date": "Datum", + "has_time": "Zäit" + }, + "input_number": { + "max": "Maximale Wäert", + "min": "Minimale Wäert", + "mode": "Affichage Modus", + "unit_of_measurement": "Moosseenheet" + }, + "input_select": { + "add": "Dobäisetzen", + "add_option": "Optioun dobäisetzen", + "no_options": "Et gi nach keng Optiounen.", + "options": "Optiounen" + }, + "input_text": { + "max": "Maximal Längt", + "min": "Minimal Längt", + "mode": "Affichage Modus", + "password": "Passwuert", + "text": "Text" + }, + "not_editable": "Net Editéierbar", + "required_error_msg": "Dëst Feld ass erfuerderlech" + }, "more_info_control": { "dismiss": "Dialog ofbriechen", "edit": "Entitéit änneren", @@ -1356,6 +1389,30 @@ } }, "header": "Home Assistant astellen", + "helpers": { + "caption": "Helper", + "description": "Elementer déi et erlaaben Automatismen z'erstellen.", + "dialog": { + "add_helper": "Helper bäifügen", + "add_platform": "{platform} bäifügen", + "create": "Erstellen" + }, + "picker": { + "add_helper": "Helper bäifügen", + "headers": { + "editable": "Editéierbar", + "name": "Numm", + "type": "Typ" + } + }, + "types": { + "input_boolean": "Ëmschalten", + "input_datetime": "Datum an/oder Zäit", + "input_number": "Nummer", + "input_select": "Auswiellëscht", + "input_text": "Text" + } + }, "integrations": { "caption": "Integratiounen", "config_entry": { @@ -1801,7 +1858,7 @@ "frontend": "frontend-ui", "frontend_version": "Frontend Versioun: {version} - {type}", "home_assistant_logo": "Home Assistant logo", - "icons_by": "Ikoner vun", + "icons_by": "Ikonen vun", "license": "Verëffentlecht ënnert der Apache 2.0 Lizenz", "lovelace_ui": "Zum Lovelace Benotzer Interface wiesselen", "path_configuration": "Pad zur configuration.yaml: {path}", @@ -1978,14 +2035,14 @@ "entity": "Entitéit", "hold_action": "Aktioun beim unhalen", "hours_to_show": "Stonnen uweisen", - "icon": "Ikone", - "icon_height": "Héicht vun der Ikone", + "icon": "Ikon", + "icon_height": "Héicht vun der Ikon", "image": "Wee zum Bild", "maximum": "Maximum", "minimum": "Minimum", "name": "Numm", "refresh_interval": "Aktualiséierungs Intervall", - "show_icon": "Ikone uweisen?", + "show_icon": "Ikon uweisen?", "show_name": "Numm uweisen?", "show_state": "Zoustand uweisen?", "tap_action": "Aktioun beim tippen", diff --git a/translations/nb.json b/translations/nb.json index 9c14ba1933..8039f96253 100644 --- a/translations/nb.json +++ b/translations/nb.json @@ -1416,7 +1416,7 @@ "input_boolean": "Veksle", "input_datetime": "Dato og/eller klokkeslett", "input_number": "Nummer", - "input_select": "Fall ned", + "input_select": "Dropdown", "input_text": "Tekst" } }, @@ -1992,7 +1992,7 @@ } }, "changed_toast": { - "message": "Lovelace UI-konfigurasjonen ble oppdatert. Oppdater for å se endringer?", + "message": "Lovelace UI-konfigurasjonen ble oppdatert. refresh for å se endringer?", "refresh": "Oppdater" }, "editor": { diff --git a/translations/pl.json b/translations/pl.json index 2f925d9069..4ff32783f8 100644 --- a/translations/pl.json +++ b/translations/pl.json @@ -626,6 +626,8 @@ "enabled_description": "Wyłączone encje nie zostaną dodane do Home Assistant'a.", "enabled_label": "Włącz encję", "entity_id": "Identyfikator encji", + "icon": "Nadpisanie ikony", + "icon_error": "Ikony powinny mieć format 'prefix:iconname', np. 'mdi:home'", "name": "Nadpisanie nazwy", "note": "Uwaga: może to jeszcze nie działać ze wszystkimi integracjami.", "unavailable": "Ta encja nie jest obecnie dostępna.", @@ -640,6 +642,44 @@ "default_confirmation_title": "Jesteś pewny?", "ok": "OK" }, + "helper_settings": { + "generic": { + "icon": "Ikona", + "initial_value": "Wartość początkowa na starcie", + "initial_value_explain": "Wartość, jaką będzie miał element po uruchomieniu Asystenta domowego. Pozostawione puste spowoduje przywrócenie poprzedniej wartości.", + "name": "Imię" + }, + "input_datetime": { + "has_date": "Data", + "has_time": "Czas" + }, + "input_number": { + "box": "Pole wyboru", + "max": "Maksymalna wartość", + "min": "Minimalna wartość", + "mode": "Tryb wyświetlania", + "slider": "Suwak", + "step": "Wielkość kroku suwaka", + "unit_of_measurement": "Jednostka miary" + }, + "input_select": { + "add": "Dodaj", + "add_option": "Dodaj opcję", + "no_options": "Nie ma jeszcze opcji.", + "options": "Opcje" + }, + "input_text": { + "max": "Maksymalna długość", + "min": "Minimalna długość", + "mode": "Tryb wyświetlania", + "password": "Hasło", + "pattern": "Wyrażenie regularne do sprawdzania poprawności po stronie klienta", + "text": "Pole tekstowe" + }, + "not_editable": "Nie edytowalne", + "not_editable_text": "Tego elementu nie można zmienić z poziomu interfejsu użytkownika, ponieważ jest on zdefiniowany w pliku configuration.yaml.", + "required_error_msg": "To pole jest wymagane" + }, "more_info_control": { "dismiss": "Zamknij okno dialogowe", "edit": "Edytuj encję", @@ -1356,6 +1396,30 @@ } }, "header": "Konfiguruj Home Assistant'a", + "helpers": { + "caption": "Pola do wprowadzania danych", + "description": "Elementy, które mogą pomóc w tworzeniu automatyzacji", + "dialog": { + "add_helper": "Dodaj pole do wprowadzania danych", + "add_platform": "Dodaj {platform}", + "create": "Utwórz" + }, + "picker": { + "add_helper": "Dodaj pole do wprowadzania danych", + "headers": { + "editable": "Edytowalne", + "name": "Nazwa", + "type": "Typ" + } + }, + "types": { + "input_boolean": "Przełączanie", + "input_datetime": "Data i / lub godzina", + "input_number": "Pole numeryczne", + "input_select": "Pole rozwijalne", + "input_text": "Pole tekstowe" + } + }, "integrations": { "caption": "Integracje", "config_entry": { @@ -2109,6 +2173,7 @@ "error_remove": "Nie można usunąć konfiguracji: {error}", "error_save_yaml": "Nie można zapisać YAML: {error}", "header": "Edytuj konfigurację", + "resources_moved": "Zasoby nie powinny być już dodawane do konfiguracji Lovelace, ale można je dodawać w panelu konfiguracji Lovelace.", "save": "Zapisz", "saved": "Zapisano", "unsaved_changes": "Niezapisane zmiany" @@ -2415,7 +2480,7 @@ "dropdown_label": "Motyw", "error_no_theme": "Brak dostępnych motywów.", "header": "Motyw", - "link_promo": "Dowiedz się więcej o motywch" + "link_promo": "Dowiedz się więcej o motywach" }, "vibrate": { "description": "Włącz lub wyłącz wibracje na tym urządzeniu podczas sterowania urządzeniami.", diff --git a/translations/zh-Hans.json b/translations/zh-Hans.json index 1af020b33d..d0e169f0f2 100644 --- a/translations/zh-Hans.json +++ b/translations/zh-Hans.json @@ -581,7 +581,7 @@ "entity": "相关实体", "group": "以下群组的一部分", "integration": "集成", - "no_related_found": "找不到相关项目。", + "no_related_found": "未找到相关项目。", "scene": "以下场景的一部分", "script": "以下脚本的一部分" }, @@ -626,12 +626,14 @@ "enabled_description": "已禁用的实体不再添加到 Home Assistant。", "enabled_label": "启用实体", "entity_id": "实体 ID", + "icon": "图标覆盖", + "icon_error": "图标的格式应为 prefix:iconname,例如:mdi:home", "name": "覆盖名称", "note": "注意:这可能不适用于所有集成。", "unavailable": "该实体暂不可用。", "update": "更新" }, - "no_unique_id": "该实体没有唯一的ID,因此无法由UI进行管理设置。", + "no_unique_id": "该实体没有唯一的 ID,因此无法由 UI 管理其设置。", "related": "关联", "settings": "设置" }, @@ -640,8 +642,46 @@ "default_confirmation_title": "您确定吗?", "ok": "确定" }, + "helper_settings": { + "generic": { + "icon": "图标", + "initial_value": "初始值", + "initial_value_explain": "该元素在 Home Assistant 启动时具有的值。如果留空,则保留之前的值。", + "name": "名称" + }, + "input_datetime": { + "has_date": "日期", + "has_time": "时间" + }, + "input_number": { + "box": "输入框", + "max": "最大值", + "min": "最小值", + "mode": "显示模式", + "slider": "滑杆", + "step": "滑杆步长", + "unit_of_measurement": "单位" + }, + "input_select": { + "add": "添加", + "add_option": "添加选项", + "no_options": "目前没有选项。", + "options": "选项" + }, + "input_text": { + "max": "最大长度", + "min": "最小长度", + "mode": "显示模式", + "password": "密码", + "pattern": "用于客户端验证的正则表达式模式", + "text": "文本" + }, + "not_editable": "不可编辑", + "not_editable_text": "该实体无法在 UI 中修改,因为它被定义于 configuration.yaml。", + "required_error_msg": "此字段为必填字段" + }, "more_info_control": { - "dismiss": "关闭对话", + "dismiss": "关闭对话框", "edit": "编辑实体", "person": { "create_zone": "从当前位置创建地点" @@ -668,7 +708,7 @@ }, "vacuum": { "clean_spot": "清理污渍", - "commands": "吸尘器清扫指令:", + "commands": "扫地机指令:", "fan_speed": "风扇转速", "locate": "定位", "pause": "暂停", @@ -761,7 +801,7 @@ "link_profile_page": "您的个人资料页面" }, "areas": { - "caption": "区域注册", + "caption": "区域注册表", "create_area": "创建区域", "description": "您家中所有区域的概览。", "editor": { @@ -1172,7 +1212,7 @@ }, "core": { "caption": "通用", - "description": "检查你的配置文件及服务器控制", + "description": "更改 Home Assistant 的通用配置", "section": { "core": { "core_config": { @@ -1191,7 +1231,7 @@ "unit_system_imperial": "英制", "unit_system_metric": "公制" }, - "header": "配置及服务器控制", + "header": "通用配置", "introduction": "更改配置的过程可能有些抓狂,我们懂的。这部分将帮助你减轻一些工作。" }, "server_control": { @@ -1212,7 +1252,7 @@ "validation": { "check_config": "检查配置", "heading": "配置检查", - "introduction": "此处可以帮助你检验最新修改的配置文件有效性", + "introduction": "此处可以帮助您检验最新修改的配置文件有效性", "invalid": "配置无效", "valid": "配置有效!" } @@ -1251,7 +1291,7 @@ }, "create": "通过设备创建自动化", "no_automations": "没有自动化", - "no_device_automations": "该设备没有任何自动化可使用。", + "no_device_automations": "该设备没有可用的自动化。", "triggers": { "caption": "当以下事件发生时:" } @@ -1295,7 +1335,7 @@ "update": "更新" }, "entities": { - "caption": "实体注册", + "caption": "实体注册表", "description": "所有已知实体的概览。", "editor": { "confirm_delete": "您确定要删除此条目吗?", @@ -1356,6 +1396,25 @@ } }, "header": "配置 Home Assistant", + "helpers": { + "dialog": { + "add_platform": "添加 {platform}" + }, + "picker": { + "headers": { + "editable": "可编辑", + "name": "名称", + "type": "类型" + } + }, + "types": { + "input_boolean": "开关", + "input_datetime": "日期/时间", + "input_number": "数值", + "input_select": "下拉菜单", + "input_text": "文本" + } + }, "integrations": { "caption": "集成", "config_entry": { @@ -1395,7 +1454,7 @@ }, "configure": "配置", "configured": "已配置", - "description": "管理连接的设备和服务", + "description": "管理并设置集成", "details": "集成详细信息", "discovered": "已发现", "home_assistant_website": "Home Assistant 网站", @@ -1416,7 +1475,7 @@ "note_about_integrations": "并非所有集成都可以通过 UI 进行配置。", "note_about_website_reference": "更多可用信息,尽在 " }, - "introduction": "你可以在此配置 Home Assistant 及组件。目前并非所有配置都能通过前端 UI 完成,但是我们在努力实现中。", + "introduction": "您可以在此配置 Home Assistant 及组件。目前并非所有配置都能通过前端 UI 完成,但是我们在努力实现中。", "person": { "add_person": "添加人员", "caption": "人员", @@ -1534,8 +1593,8 @@ }, "validation": { "check_config": "检查配置", - "heading": "配置有效性", - "introduction": "此处可以帮助你检验最新修改的配置文件有效性", + "heading": "配置检查", + "introduction": "此处可以帮助您检验最新修改的配置文件有效性", "invalid": "配置无效", "valid": "配置有效!" } @@ -1706,9 +1765,9 @@ "update": "更新" }, "edit_home_zone": "住址的半径目前不能在前端设定。请在地图上拖动标示以移动家的区域。", - "edit_home_zone_narrow": "住址的半径尚无法由前端进行设定、但可以于一般设定中变更其位置。", - "go_to_core_config": "转换至常规配置?", - "home_zone_core_config": "住址的位置可以于一般设定页面中进行编辑、但半径尚无法编辑。是否要切换至一般设定?", + "edit_home_zone_narrow": "住址区域的半径尚无法在前端设定,但可以在通用配置中更改其位置。", + "go_to_core_config": "前往通用配置?", + "home_zone_core_config": "家的位置可以在通用配置中进行编辑,但半径尚无法在前端设定。是否前往通用配置?", "introduction": "地点用于定义世界的某个地方。若某人位于一个地点,则其状态的名称就取自该地点。地点也可用作自动化配置中的触发条件和环境条件。", "no_zones_created_yet": "看来您还没有建立地点。" }, @@ -1796,7 +1855,7 @@ "info": { "built_using": "建于", "custom_uis": "自定义用户界面:", - "default_ui": "{action} {name}为此设备上的默认页面", + "default_ui": "{action} {name} 为此设备上的默认页面", "developed_by": "由一帮很 Awesome~~~ 的人开发。", "frontend": "前端用户界面", "frontend_version": "前端版本: {version} - {type}", @@ -1829,7 +1888,7 @@ "description_publish": "发送一个数据包", "listening_to": "监听", "message_received": "{time} 收到关于 {topic} 的消息[ {id} ]", - "payload": "有效负载(允许模板)", + "payload": "负载参数(可使用模板)", "publish": "发送", "start_listening": "开始监听", "stop_listening": "停止监听", @@ -2084,7 +2143,9 @@ "header": "查看配置", "header_name": "{name}查看配置", "move_left": "向左移动视图", - "move_right": "向右移动视图" + "move_right": "向右移动视图", + "tab_badges": "徽章", + "tab_settings": "设置" }, "header": "编辑 UI", "menu": { @@ -2103,9 +2164,9 @@ "confirm_unsaved_changes": "您有未保存的更改,确定要退出吗?", "confirm_unsaved_comments": "您的配置包含注释,这些注释将不会保存。是否继续?", "error_invalid_config": "您的配置无效:{error}", - "error_parse_yaml": "无法解析YAML:{error}", + "error_parse_yaml": "无法解析 YAML:{error}", "error_remove": "无法删除配置:{error}", - "error_save_yaml": "无法保存YAML:{error}", + "error_save_yaml": "无法保存 YAML:{error}", "header": "编辑配置", "save": "保存", "saved": "已保存", @@ -2140,7 +2201,7 @@ }, "reload_lovelace": "重新加载 Lovelace", "unused_entities": { - "available_entities": "这些是您可用的实体,但尚未在Lovelace UI中显示。", + "available_entities": "这些是您可用的实体,但尚未在 Lovelace UI 中显示。", "domain": "域", "entity": "实体", "entity_id": "实体 ID", @@ -2310,7 +2371,7 @@ "intro": "{name},您好!欢迎来到 Home Assistant。您想怎样命名您的家呢?", "intro_location": "我们想知道您住在哪里。这将用于显示资讯以及设置基于太阳的自动化。此数据永远不会在您的网络以外共享。", "intro_location_detect": "我们可以通过向外部服务发出一个一次性请求来帮助您填写此信息。", - "location_name_default": "家" + "location_name_default": "我的家" }, "integration": { "finish": "完成", @@ -2416,8 +2477,8 @@ "link_promo": "了解主题" }, "vibrate": { - "description": "控制设备时,在此设备上启用或禁用抖动。", - "header": "抖动" + "description": "控制设备时,在此设备上启用或禁用振动。", + "header": "振动" } }, "shopping-list": { diff --git a/translations/zh-Hant.json b/translations/zh-Hant.json index 3ccbb90a53..10e2377b1e 100644 --- a/translations/zh-Hant.json +++ b/translations/zh-Hant.json @@ -1992,7 +1992,7 @@ } }, "changed_toast": { - "message": "Lovelace UI 設定已更新,是否要更新頁面?", + "message": "此 Lovelace UI 設定已更新,是否要更新頁面檢視變更?", "refresh": "更新" }, "editor": { From 42f311a4574e1db4ceb0381a0c1fa61b9a149afc Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sat, 29 Feb 2020 14:09:14 +0100 Subject: [PATCH 03/36] Update whitelist (#5026) --- src/data/device_automation.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/data/device_automation.ts b/src/data/device_automation.ts index 61b87d8852..376e6f0ae6 100644 --- a/src/data/device_automation.ts +++ b/src/data/device_automation.ts @@ -66,7 +66,15 @@ export const fetchDeviceTriggerCapabilities = ( trigger, }); -const whitelist = ["above", "below", "brightness", "code", "for"]; +const whitelist = [ + "above", + "below", + "brightness", + "code", + "for", + "position", + "set_brightness", +]; export const deviceAutomationsEqual = ( a: DeviceAutomation, From 226e6e9f595b88c78d81c5c6cf74d2d495cb1414 Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Sun, 1 Mar 2020 00:32:29 +0000 Subject: [PATCH 04/36] [ci skip] Translation update --- translations/ca.json | 1 + translations/lv.json | 185 +++++++++++++++++++++----------------- translations/ru.json | 76 +++++++++++++++- translations/zh-Hans.json | 81 ++++++++++++++++- 4 files changed, 257 insertions(+), 86 deletions(-) diff --git a/translations/ca.json b/translations/ca.json index 0ec459370f..55041c8f2d 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -2116,6 +2116,7 @@ }, "save_config": { "cancel": "M'ho he repensat", + "close": "Tanca", "header": "Pren el control de la interfície d'usuari Lovelace", "para": "De manera predeterminada, Home Assistant mantindrà al dia la teva interfície d'usuari, actualitzant-la quan hi hagi noves entitats o nous components de Lovelace disponibles. Si prens el control, ja no es faran aquests canvis automàticament.", "para_sure": "Estàs segur que vols prendre el control de la interfície d'usuari?", diff --git a/translations/lv.json b/translations/lv.json index f04745b203..2256fb597a 100644 --- a/translations/lv.json +++ b/translations/lv.json @@ -58,7 +58,7 @@ }, "groups": { "system-admin": "Administratori", - "system-read-only": "Tikai-Lasīt Lietotāji", + "system-read-only": "Lietotāji ar tiesībām tikai lasīt", "system-users": "Lietotāji" }, "panel": { @@ -77,7 +77,7 @@ "state_attributes": { "climate": { "fan_mode": { - "auto": "Automātisks", + "auto": "Auto", "off": "Izslēgts", "on": "Ieslēgts" }, @@ -106,11 +106,11 @@ "armed": "Pieslēgta", "armed_away": "Pieslēgta", "armed_custom_bypass": "Pieslēgta", - "armed_home": "Piesl.", + "armed_home": "Pieslēgta", "armed_night": "Pieslēgta", "arming": "Pieslēdz", - "disarmed": "Atslēgt", - "disarming": "Atslēgt", + "disarmed": "Atslēgta", + "disarming": "Atslēdzas", "pending": "Gaida", "triggered": "Trig" }, @@ -131,16 +131,16 @@ }, "state": { "alarm_control_panel": { - "armed": "Pieslēgts", - "armed_away": "Pieslēgts uz prombūtni", + "armed": "Pieslēgta", + "armed_away": "Pieslēgta uz prombūtni", "armed_custom_bypass": "Pieslēgts pielāgots apvedceļš", - "armed_home": "Pieslēgts mājās", - "armed_night": "Pieslēgts uz nakti", - "arming": "Pieslēdzu", - "disarmed": "Atslēgts", - "disarming": "Atslēdzu", + "armed_home": "Pieslēgta mājās", + "armed_night": "Pieslēgta uz nakti", + "arming": "Pieslēdzas", + "disarmed": "Atslēgta", + "disarming": "Atslēdzas", "pending": "Gaida", - "triggered": "Aktivizēts" + "triggered": "Aktivizēta" }, "automation": { "off": "Izslēgts", @@ -349,7 +349,7 @@ "timer": { "active": "aktīvs", "idle": "dīkstāve", - "paused": "apturēta" + "paused": "apturēts" }, "vacuum": { "cleaning": "Notiek uzkopšana", @@ -387,7 +387,7 @@ }, "query_stage": { "dead": "Beigta ({query_stage})", - "initializing": "Inicializē ( {query_stage} )" + "initializing": "Inicializē ({query_stage})" } } }, @@ -395,14 +395,14 @@ "auth_store": { "ask": "Vai vēlaties saglabāt šo pieteikšanos?", "confirm": "Saglabāt pieteikšanos", - "decline": "Nē paldies" + "decline": "Nē, paldies" }, "card": { "alarm_control_panel": { - "arm_away": "Pieslēgt prombūtni", + "arm_away": "Prombūtnes režīms", "arm_custom_bypass": "Pielāgots apvedceļš", - "arm_home": "Pieslēgt mājas", - "arm_night": "Pieslēgts uz nakti", + "arm_home": "Mājās režīms", + "arm_night": "Pieslēgta uz nakti", "armed_custom_bypass": "Pielāgots apvedceļš", "clear_code": "Notīrīt", "code": "Kods", @@ -612,7 +612,7 @@ "password": "Parole", "text": "Teksts" }, - "not_editable": "Nav labojams", + "not_editable": "Nav rediģējams", "required_error_msg": "Šis lauks ir obligāts" }, "more_info_control": { @@ -621,7 +621,7 @@ "create_zone": "Izveidot zonu no pašreizējās atrašanās vietas" }, "script": { - "last_action": "Pēdējā Darbība" + "last_action": "Pēdējā darbība" }, "settings": "Vienības iestatījumi", "sun": { @@ -663,8 +663,8 @@ "reconfigure": "Pārkonfigurēt ierīci", "remove": "Noņemt ierīci" }, - "manuf": "autors {manufacturer}", - "no_area": "Nav Apgabala", + "manuf": "{manufacturer}", + "no_area": "Nav apgabala", "services": { "reconfigure": "Pārkonfigurējiet ZHA ierīci ( labot ierīci). Izmantojiet to, ja rodas problēmas ar ierīci. Ja attiecīgā ierīce ir ar akumulatoru darbināma ierīce, lūdzu, pārliecinieties, ka tā ir nomodā un pieņem komandas, kad izmantojat šo pakalpojumu.", "remove": "Noņemt ierīci no Zigbee tīkla.", @@ -673,7 +673,7 @@ "zha_device_card": { "area_picker_label": "Apgabals", "device_name_placeholder": "Lietotāja dots vārds", - "update_name_button": "Atjaunināt Vārdu" + "update_name_button": "Atjaunināt nosaukumu" } } }, @@ -724,7 +724,7 @@ "header": "Apgabali", "integrations_page": "Integrāciju lapa", "introduction": "Apgabali tiek izmantoti, lai organizētu ierīces atrašanās vietu. Šī informācija tiks izmantota daudzviet Home Assistant, lai palīdzētu organizēt lietotāja saskarni, atļaujas un integrācijas ar citām sistēmām.", - "introduction2": "Lai piesaistītu ierīces noteiktam apgabalam, izmantojiet zemāk esošo saiti, lai pārietu uz integrāciju lapu un tad noklikšķiniet uz konfigurētas integrācijas, lai nonāktu pie ierīču kartiņu saraksta.", + "introduction2": "Lai piesaistītu ierīces noteiktam apgabalam, izmantojiet zemāk esošo saiti, lai pārietu uz integrāciju lapu un tad noklikšķiniet uz konfigurētas integrācijas, lai nonāktu pie ierīču kartīšu saraksta.", "no_areas": "Izskatās, ka vēl neesat izveidojis nevienu apgabalu." } }, @@ -770,8 +770,8 @@ }, "wait_template": { "label": "Pagaidīt", - "timeout": "Taimauts (pēc izvēles)", - "wait_template": "Pagaidīt Veidne" + "timeout": "Taimauts (nav obligāts)", + "wait_template": "Gaidīšanas veidne" } }, "unsupported_action": "Neatbalstīta darbība: {action}" @@ -783,11 +783,14 @@ "delete_confirm": "Vai tiešām vēlaties dzēst?", "duplicate": "Duplicēt", "header": "Nosacījumi", - "introduction": "Nosacījumi ir automatizācijas noteikuma izvēles daļa, un to var izmantot, lai novērstu darbību rašanos. Nosacījumi izskatās ļoti līdzīgi trigeriem, taču tie ir ļoti atšķirīgi. Trigeri izskatīs notikumus, kas notiek sistēmā, bet nosacījums tikai aplūko, kā sistēma šobrīd izskatās. Trigeri var novērot, ka slēdzis tiek ieslēgts. Nosacījums var redzēt tikai to, vai slēdzis pašlaik ir ieslēgts vai izslēgts.", + "introduction": "Nosacījumi nav obligāti, tos var izmantot, lai novērstu automatizācijas tālāku izpildi, ja vien nav izpildījušies visi priekšnosacījumi.", "learn_more": "Uzziniet vairāk par nosacījumiem", "name": "Nosacījums", "type_select": "Nosacījuma tips", "type": { + "and": { + "label": "Un" + }, "device": { "extra_fields": { "above": "Virs", @@ -802,15 +805,18 @@ "label": "Skaitliskais stāvoklis", "value_template": "Vērtības veidne (neobligāti)" }, + "or": { + "label": "Vai" + }, "state": { "label": "Stāvoklis", "state": "Stāvoklis" }, "sun": { "after": "Pēc:", - "after_offset": "Pēc nobīdes (neobligāti)", + "after_offset": "Nobīde pēc (nav obligāta)", "before": "Pirms:", - "before_offset": "Pirms nobīdes (neobligāti)", + "before_offset": "Nobīde pirms (nav obligāta)", "label": "Saule", "sunrise": "Saullēkts", "sunset": "Saulriets" @@ -839,8 +845,9 @@ }, "edit_ui": "Rediģēt, izmantojot UI", "edit_yaml": "Rediģēt kā YAML", + "enable_disable": "Iespējot/atspējot automatizāciju", "introduction": "Lietojiet automatizācijas, lai iedzīvinātu Jūsu mājās", - "load_error_not_editable": "Automatizācijas ir rediģējamas tikai \"automations.yaml\" failā.", + "load_error_not_editable": "Tikai \"automations.yaml\" failā esošās automatizācijas ir rediģējamas.", "load_error_unknown": "Kļūda ielādējot automatizāciju ({err_no})", "save": "Saglabāt", "triggers": { @@ -855,6 +862,11 @@ "type_select": "Trigera veids", "type": { "device": { + "extra_fields": { + "above": "Virs", + "below": "Zem", + "for": "Ilgums" + }, "label": "Ierīce" }, "event": { @@ -878,7 +890,7 @@ }, "mqtt": { "label": "MQTT", - "payload": "Vērtība (pēc izvēles)", + "payload": "Vērtība (nav obligāta)", "topic": "Temats" }, "numeric_state": { @@ -896,7 +908,7 @@ "sun": { "event": "Notikums:", "label": "Saule", - "offset": "Nobīde (pēc izvēles)", + "offset": "Nobīde (nav obligāta)", "sunrise": "Saullēkts", "sunset": "Saulriets" }, @@ -920,7 +932,7 @@ }, "zone": { "enter": "Ieiet", - "entity": "Vienība ar atrašanās vietu", + "entity": "Vienība ar atrašanās vietas datiem", "event": "Notikums:", "label": "Zona", "leave": "Iziet", @@ -964,6 +976,12 @@ "dialog_certificate": { "close": "Aizvērt" }, + "forgot_password": { + "email_error_msg": "Nederīgs e-pasts" + }, + "login": { + "email_error_msg": "Nederīgs e-pasts" + }, "register": { "create_account": "Izveidot kontu", "email_address": "E-pasta adrese", @@ -1032,7 +1050,7 @@ "introduction": "Uzlabojiet atribūtus katrai vienībai. Pievienotie jeb mainīties pielāgojumi stāsies spēkā nekavējoties. Noņemtie pielāgojumi stāsies spēkā, kad vienība tiks atjaunināta." }, "warning": { - "not_applied": "Veiktās izmaiņas ir saglabātas, bet netiks piemērotas veicot konfigurācijas pārlādēšanu, ja vien tajā include." + "not_applied": "Veiktās izmaiņas ir saglabātas, bet netiks piemērotas pēc konfigurācijas pārlādēšanas, ja tajā nav include." } }, "devices": { @@ -1110,11 +1128,11 @@ "show_disabled": "Rādīt atspējotās vienības", "status": { "disabled": "Atspējota", - "ok": "Ok", + "ok": "Labi", "readonly": "Tikai lasāma", "unavailable": "Nepieejama" }, - "unavailable": "(nav pieejams)" + "unavailable": "(nav pieejama)" } }, "header": "Home Assistant konfigurēšana", @@ -1250,12 +1268,12 @@ "delete_confirm": "Vai tiešām vēlaties izdzēst šo ainu?", "delete_scene": "Dzēst ainu", "edit_scene": "Rediģēt ainu", - "header": "Ainu Redaktors", + "header": "Ainu redaktors", "introduction": "Ainu redaktors ļauj Jums izveidot un rediģēt ainas. Lūdzu, sekojiet saitei zemāk, lai izlasītu instrukciju, ka Jūs esat pareizi konfigurējis Home Assistant.", "learn_more": "Uzzināt vairāk par ainām", "no_scenes": "Mēs nevarējām atrast nevienu rediģējamu ainu", "only_editable": "Rediģēt var tikai ainas, kas definētas scenes.yaml.", - "pick_scene": "Izvēlaties ainu kuru rediģēt", + "pick_scene": "Izvēlaties ainu rediģēšanai", "show_info_scene": "Rādīt informāciju par ainu" } }, @@ -1310,7 +1328,7 @@ "validation": { "check_config": "Pārbaudīt konfigurāciju", "heading": "Konfigurācijas pārbaude", - "introduction": "Veiciet konfigurācijas pārbaudi, ja nesen esat veicis izmaiņas konfigurācijā un vēlaties pārliecināties, ka tā ir korekta", + "introduction": "Veiciet konfigurācijas pārbaudi, ja nesen esat veicis izmaiņas konfigurācijā un vēlaties pārliecināties, ka tā nesatur kļūdas", "invalid": "Konfigurācija kļūdaina", "valid": "Konfigurācija korekta!" } @@ -1374,7 +1392,7 @@ "device_card": { "area_picker_label": "Apgabals", "device_name_placeholder": "Lietotāja dots nosaukums", - "update_name_button": "Atjaunināt Nosaukumu" + "update_name_button": "Atjaunināt nosaukumu" }, "groups": { "caption": "Grupas", @@ -1433,16 +1451,16 @@ }, "description": "Pārvaldiet Z-Wave tīklu", "network_management": { - "header": "Z-Wave Tīkla Pārvaldība", + "header": "Z-Wave tīkla pārvaldība", "introduction": "Palaist komandas, kas ietekmē Z-Wave tīklu. Jūs nesaņemsit atpakaļsaiti par to, vai vairums komandu ir izdevušās, taču varat pārbaudīt OZW žurnālu, lai mēģinātu to uzzināt." }, "network_status": { - "network_started": "Z-Wave Tīkls Sākts", + "network_started": "Z-Wave tīkls startēts", "network_started_note_all_queried": "Visi mezgli ir ierindoti.", "network_started_note_some_queried": "Nomodā esoši megli ir ierindoti. Miega režimā esoši mezgli tiks ierindoti tiklīdz tie būs nomodā.", "network_starting": "Z-Wave tīkla startēšana...", "network_starting_note": "Tas var aizņemt kādu laiku atkarībā no Jūsu tīkla lieluma.", - "network_stopped": "Z-Wave Tīkls Apturēts" + "network_stopped": "Z-Wave tīkls apturēts" }, "node_config": { "config_parameter": "Konfigurācijas parametrs", @@ -1455,16 +1473,16 @@ "true": "Patiess" }, "services": { - "add_node": "Pievienot Mezglu", - "add_node_secure": "Pievienot Drošo Mezglu", - "cancel_command": "Atcelt Komandu", - "heal_network": "Labot Tīklu", - "remove_node": "Noņemt Mezglu", + "add_node": "Pievienot mezglu", + "add_node_secure": "Pievienot drošo mezglu", + "cancel_command": "Atcelt komandu", + "heal_network": "Labot tīklu", + "remove_node": "Noņemt mezglu", "save_config": "Saglabāt konfigurāciju", - "soft_reset": "Vieglā Atiestatīšana", - "start_network": "Sākt Tīklu", - "stop_network": "Apturēt Tīklu", - "test_network": "Pārbaudīt Tīklu" + "soft_reset": "Vieglā atiestatīšana", + "start_network": "Startēt tīklu", + "stop_network": "Apturēt tīklu", + "test_network": "Pārbaudīt tīklu" }, "values": { "header": "Mezglu vērtības" @@ -1484,6 +1502,7 @@ "title": "Notikumi" }, "info": { + "lovelace_ui": "Doties uz Lovelace UI", "server": "serveris", "source": "Avots:", "title": "Informācija" @@ -1546,7 +1565,7 @@ } }, "changed_toast": { - "message": "Lovelace konfigurācija tika atjaunināta. Vai vēlaties atsvaidzināt?", + "message": "Šī informācijas paneļa Lovelace UI konfigurācija tika atjaunināta. Vēlaties atsvaidzināt, lai redzētu izmaiņas?", "refresh": "Atsvaidzināt" }, "editor": { @@ -1584,11 +1603,11 @@ }, "edit_card": { "add": "Pievienot kartīti", - "delete": "Dzēst Kartiņu", + "delete": "Dzēst kartīti", "edit": "Rediģēt", "header": "Kartītes konfigurācija", - "move": "Pārvietot uz Skatu", - "pick_card": "Kuru kartiņu vēlaties pievienot?", + "move": "Pārvietot uz skatu", + "pick_card": "Kuru kartīti vēlaties pievienot?", "save": "Saglabāt", "toggle_editor": "Pārslēgt redaktoru" }, @@ -1608,6 +1627,7 @@ }, "header": "Rediģēt lietotāja saskarni", "menu": { + "open": "Atvērt Lovelace UI izvēlni", "raw_editor": "Konfigurācijas teksta redaktors" }, "migrate": { @@ -1617,10 +1637,10 @@ "para_no_id": "Šim elementam nav ID. Lūdzu, pievienojiet ID šim elementam 'ui-lovelace.yaml' failā." }, "raw_editor": { - "confirm_unsaved_changes": "Jums ir nesaglabātas izmaiņas, Jūs esat pārliecināts, ka vēlaties iziet?", - "confirm_unsaved_comments": "Jūsu konfigurācija satur komentāru(s), tie netiks saglabāti. Vai Jūs vēlaties turpināt?", - "error_invalid_config": "Jūsu konfigurācija nav derīga: {error}", - "error_parse_yaml": "Nevar izanalizēt YAML: {error}", + "confirm_unsaved_changes": "Jums ir nesaglabātas izmaiņas. Vai esat pārliecināts, ka vēlaties iziet?", + "confirm_unsaved_comments": "Jūsu konfigurācija satur komentāru(s), tie netiks saglabāti. Vai vēlaties turpināt?", + "error_invalid_config": "Jūsu konfigurācija satur kļūdas: {error}", + "error_parse_yaml": "Nevar noparsēt YAML: {error}", "error_save_yaml": "Nevar saglabāt YAML: {error}", "header": "Konfigurācijas redaktors", "save": "Saglabāt", @@ -1629,8 +1649,9 @@ }, "save_config": { "cancel": "Atcelt", + "close": "Aizvērt", "header": "Pārņemt kontroli pār savu Lovelace lietotāja saskarni", - "para": "Pēc noklusējuma Home Assistant uzturēs jūsu lietotāja saskarni, atjauninot to, kad būs pieejami jauni objekti vai Lovelace komponenti. Ja jūs uzņematies kontroli, mēs jums vairs neveiksim izmaiņas automātiski jūsu vietā.", + "para": "Pēc noklusējuma Home Assistant uzturēs jūsu lietotāja saskarni, atjauninot to, kad būs pieejamas jaunas vienības vai Lovelace UI komponenti. Ja jūs uzņematies kontroli, mēs jums vairs neveiksim izmaiņas automātiski jūsu vietā.", "para_sure": "Vai tiešām vēlaties pārņemt kontroli pār lietotāja saskarni?", "save": "Pārņemt kontroli" } @@ -1643,9 +1664,9 @@ "refresh": "Atsvaidzināt", "unused_entities": "Neizmantotās vienības" }, - "reload_lovelace": "Pārlādēt Lovelace", + "reload_lovelace": "Pārlādēt lietotāja saskarni", "unused_entities": { - "available_entities": "Šīs ir vienības, kuras Jums ir pieejamas, bet kuras vēl nav Jūsu Lovelace UI.", + "available_entities": "Šīs ir vienības, kuras Jums ir pieejamas, bet kuras vēl nav Lovelace UI.", "domain": "Domēns", "entity": "Vienība", "entity_id": "Vienības ID", @@ -1654,7 +1675,7 @@ "title": "Neizmantotās vienības" }, "warning": { - "entity_non_numeric": "Vienībai nav skaitļu: {entity}", + "entity_non_numeric": "Vienība nav skaitliska: {entity}", "entity_not_found": "Vienība nav pieejama: {entity}" } }, @@ -1686,9 +1707,9 @@ }, "mfa": { "data": { - "code": "Divpakāpju Autorizācijas Kods" + "code": "Divpakāpju autorizācijas kods" }, - "description": "Savā ierīcē atveriet ** {mfa_module_name} **, lai skatītu divu faktoru autentifikācijas kodu un verificētu savu identitāti:" + "description": "Atveriet **{mfa_module_name}** savā ierīcē, lai skatītu divu faktoru autentifikācijas kodu un verificētu savu identitāti:" } } }, @@ -1711,13 +1732,13 @@ "data": { "code": "Divu faktoru autentifikācijas kods" }, - "description": "Savā ierīcē atveriet ** {mfa_module_name} **, lai skatītu divu faktoru autentifikācijas kodu un verificētu savu identitāti:" + "description": "Atveriet **{mfa_module_name}** savā ierīcē, lai skatītu divu faktoru autentifikācijas kodu un verificētu savu identitāti:" } } }, "legacy_api_password": { "abort": { - "login_expired": "Sesija beigusies, lūdzu piesakaties vēlreiz.", + "login_expired": "Sesija beigusies. Lūdzu, piesakaties vēlreiz.", "no_api_password_set": "Jums nav konfigurēta API parole." }, "error": { @@ -1735,7 +1756,7 @@ "data": { "code": "Divu faktoru autentifikācijas kods" }, - "description": "Savā ierīcē atveriet ** {mfa_module_name} **, lai skatītu divu faktoru autentifikācijas kodu un verificētu savu identitāti:" + "description": "Atveriet **{mfa_module_name}** savā ierīcē, lai skatītu divu faktoru autentifikācijas kodu un verificētu savu identitāti:" } } }, @@ -1757,7 +1778,7 @@ "working": "Lūdzu uzgaidiet" }, "initializing": "Inicializēšana", - "logging_in_with": "Pieteikšanās ar ** {authProviderName} **.", + "logging_in_with": "Pieteikšanās ar **{authProviderName}**.", "pick_auth_provider": "Vai pieteikties, izmantojot" }, "page-demo": { @@ -1774,15 +1795,15 @@ "labels": { "activity": "Aktivitāte", "air": "Gaiss", - "commute_home": "Brauciens uz Mājām", + "commute_home": "Brauciens uz mājām", "entertainment": "Izklaide", - "hdmi_input": "HDMI Ievade", - "hdmi_switcher": "HDMI Pārslēdzējs", + "hdmi_input": "HDMI ievade", + "hdmi_switcher": "HDMI pārslēdzējs", "information": "Informācija", "lights": "Gaismas", - "morning_commute": "Rīta Brauciens", - "total_tv_time": "Kopējais TV Laiks", - "turn_tv_off": "Izslēgt Televizoru", + "morning_commute": "Rīta brauciens", + "total_tv_time": "Kopējais TV laiks", + "turn_tv_off": "Izslēgt televizoru", "volume": "Skaļums" }, "names": { @@ -1790,7 +1811,7 @@ "hallway": "Gaitenis", "kitchen": "Virtuve", "left": "Pa kreisi", - "master_bedroom": "Galvenā Guļamistaba", + "master_bedroom": "Galvenā guļamistaba", "mirror": "Spogulis", "patio": "Terase", "right": "Pa labi", @@ -1819,11 +1840,11 @@ }, "intro": "Vai esat gatavs pamodināt savu māju, atgūt savu konfidencialitāti un pievienoties pasaules mēroga kopienai?", "user": { - "create_account": "Izveidot Kontu", + "create_account": "Izveidot kontu", "data": { "name": "Vārds", "password": "Parole", - "password_confirm": "Apstipriniet Paroli", + "password_confirm": "Apstipriniet paroli", "username": "Lietotājvārds" }, "error": { @@ -1873,7 +1894,7 @@ "learn_auth_requests": "Uzziniet, kā veikt autentificētus pieprasījumus.", "not_used": "Nekad nav izmantots", "prompt_copy_token": "Kopējiet savu piekļuves pilnvaru. Tas vairs netiks rādīts.", - "prompt_name": "Vārds?" + "prompt_name": "Nosaukums?" }, "mfa_setup": { "close": "Aizvērt", @@ -1925,7 +1946,7 @@ } }, "sidebar": { - "external_app_configuration": "Lietotņu Konfigurēšana", + "external_app_configuration": "Lietotņu konfigurēšana", "log_out": "Iziet" } } diff --git a/translations/ru.json b/translations/ru.json index b19e68a292..627c39151d 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -638,6 +638,7 @@ }, "generic": { "cancel": "Отмена", + "close": "Закрыть", "default_confirmation_title": "Вы уверены?", "ok": "ОК" }, @@ -1480,6 +1481,76 @@ "note_about_website_reference": "Все доступные интеграции Вы можете найти на " }, "introduction": "Здесь можно настроить Home Assistant. Пока что не все настройки доступны из интерфейса, но мы работаем над этим.", + "lovelace": { + "caption": "Панели Lovelace", + "dashboards": { + "cant_edit_yaml": "Панели, определенные в YAML, нельзя редактировать из пользовательского интерфейса. Вы можете изменить их в файле configuration.yaml.", + "caption": "Панели", + "conf_mode": { + "storage": "Интерфейс", + "yaml": "Файл YAML" + }, + "confirm_delete": "Вы уверены, что хотите удалить эту панель?", + "detail": { + "create": "Создать", + "delete": "Удалить", + "dismiss": "Закрыть", + "edit_dashboard": "Редактировать панель", + "icon": "Значок для боковой панели", + "new_dashboard": "Добавить новую панель", + "require_admin": "Только для администраторов", + "show_sidebar": "Показывать на боковой панели", + "title": "Название для боковой панели", + "update": "Обновить", + "url": "URL-адрес", + "url_error_msg": "URL не может содержать пробелы или специальные символы, кроме символов _ и -" + }, + "picker": { + "add_dashboard": "Добавить панель", + "headers": { + "conf_mode": "Способ управления", + "filename": "Имя файла", + "require_admin": "Только для администраторов", + "sidebar": "Показывать на боковой панели", + "title": "Название" + }, + "open": "Открыть панель" + } + }, + "description": "Настройка панелей пользовательского интерфейса Lovelace.", + "resources": { + "cant_edit_yaml": "Вы используете Lovelace в режиме YAML, поэтому невозможно управление ресурсами через пользовательский интерфейс. В данном режиме управление осуществляется через файл configuration.yaml.", + "caption": "Ресурсы", + "confirm_delete": "Вы уверены, что хотите удалить этот ресурс?", + "detail": { + "create": "Создать", + "delete": "Удалить", + "dismiss": "Закрыть", + "new_resource": "Добавить новый ресурс", + "type": "Тип ресурса", + "update": "Обновить", + "url": "URL-адрес", + "url_error_msg": "URL-адрес является обязательным полем.", + "warning_header": "Будьте осторожны!", + "warning_text": "Добавление ресурсов может быть опасным. Убедитесь, что Вы знаете источник ресурса и доверяете ему. Плохие ресурсы могут нанести серьёзный вред Вашей системе." + }, + "picker": { + "add_resource": "Добавить ресурс", + "headers": { + "type": "Тип", + "url": "URL-адрес" + } + }, + "refresh_body": "Вы должны обновить страницу, чтобы завершить удаление. Обновить сейчас?", + "refresh_header": "Обновить страницу?", + "types": { + "css": "Таблица стилей", + "html": "HTML (устарело)", + "js": "Файл JavaScript (устарело)", + "module": "Модуль JavaScript" + } + } + }, "person": { "add_person": "Добавить персону", "caption": "Люди", @@ -2171,10 +2242,13 @@ }, "save_config": { "cancel": "Оставить как есть", + "close": "Закрыть", "header": "Получение контроля над пользовательским интерфейсом", "para": "По умолчанию Home Assistant будет обслуживать Ваш пользовательский интерфейс, автоматически добавляя новые объекты и новые компоненты Lovelace, если они доступны. Если Вы получите контроль над пользовательским интерфейсом, изменения больше не будут вноситься автоматически.", "para_sure": "Вы уверены, что хотите самостоятельно контролировать пользовательский интерфейс?", - "save": "Получить контроль" + "save": "Получить контроль", + "yaml_control": "Чтобы получить контроль в режиме YAML, создайте файл с именем, указанным в настройках этой панели (по умолчанию 'ui-lovelace.yaml').", + "yaml_mode": "Вы не можете изменять конфигурацию Lovelace из пользовательского интерфейса, так как он используется в режиме YAML. Если Вы всё же хотите редактировать Lovelace из пользовательского интерфейса, удалите строку 'mode: yaml' в файле 'configuration.yaml'." }, "suggest_card": { "add": "Подтвердить", diff --git a/translations/zh-Hans.json b/translations/zh-Hans.json index d0e169f0f2..000302303e 100644 --- a/translations/zh-Hans.json +++ b/translations/zh-Hans.json @@ -364,13 +364,13 @@ }, "weather": { "clear-night": "夜间晴朗", - "cloudy": "多云", + "cloudy": "阴", "exceptional": "特殊", "fog": "雾", "hail": "冰雹", "lightning": "雷电", "lightning-rainy": "雷阵雨", - "partlycloudy": "局部多云", + "partlycloudy": "多云", "pouring": "暴雨", "rainy": "雨", "snowy": "雪", @@ -639,6 +639,7 @@ }, "generic": { "cancel": "取消", + "close": "关闭", "default_confirmation_title": "您确定吗?", "ok": "确定" }, @@ -1476,6 +1477,76 @@ "note_about_website_reference": "更多可用信息,尽在 " }, "introduction": "您可以在此配置 Home Assistant 及组件。目前并非所有配置都能通过前端 UI 完成,但是我们在努力实现中。", + "lovelace": { + "caption": "Lovelace 仪表盘", + "dashboards": { + "cant_edit_yaml": "定义于 YAML 中的仪表盘不能从 UI 修改。请在 configuration.yaml 中修改它们。", + "caption": "仪表盘", + "conf_mode": { + "storage": "UI 控制", + "yaml": "YAML 文件" + }, + "confirm_delete": "您确定要删除此仪表盘吗?", + "detail": { + "create": "创建", + "delete": "删除", + "dismiss": "关闭", + "edit_dashboard": "编辑仪表盘", + "icon": "侧边栏图标", + "new_dashboard": "添加新仪表盘", + "require_admin": "仅限管理员", + "show_sidebar": "在侧边栏显示", + "title": "侧边栏标题", + "update": "更新", + "url": "网址", + "url_error_msg": "网址中不可包含除 - 和 _ 以外的特殊字符及空格" + }, + "picker": { + "add_dashboard": "添加仪表盘", + "headers": { + "conf_mode": "配置方式", + "filename": "文件名", + "require_admin": "仅限管理员", + "sidebar": "在侧边栏显示", + "title": "标题" + }, + "open": "打开仪表盘" + } + }, + "description": "配置 Lovelace 仪表盘", + "resources": { + "cant_edit_yaml": "您正在 YAML 模式下使用 Lovelace,因此无法通过 UI 管理资源。请在 configuration.yaml 中管理它们。", + "caption": "资源", + "confirm_delete": "您确定要删除此资源吗?", + "detail": { + "create": "创建", + "delete": "删除", + "dismiss": "关闭", + "new_resource": "添加新资源", + "type": "资源类型", + "update": "更新", + "url": "网址", + "url_error_msg": "网址为必填项", + "warning_header": "注意!", + "warning_text": "添加资源可能带来危险。请确保您知道资源的来源并信任它们。恶意资源会严重损害您的系统。" + }, + "picker": { + "add_resource": "添加资源", + "headers": { + "type": "类型", + "url": "网址" + } + }, + "refresh_body": "您必须刷新页面才能完成删除,是否要立即刷新?", + "refresh_header": "要刷新吗?", + "types": { + "css": "样式表", + "html": "HTML(已过时)", + "js": "JavaScript文件(已过时)", + "module": "JavaScript 模块" + } + } + }, "person": { "add_person": "添加人员", "caption": "人员", @@ -2174,10 +2245,14 @@ }, "save_config": { "cancel": "算了吧", + "close": "关闭", "header": "自行编辑您的 Lovelace UI", "para": "默认情况下,Home Assistant 将维护您的用户界面,并在新的实体或 Lovelace 组件可用时更新它。如果您选择自行编辑,我们将不再自动为您进行更改。", "para_sure": "您确定要自行编辑用户界面吗?", - "save": "自行编辑" + "save": "自行编辑", + "yaml_config": "为方便您开始,以下是此仪表盘的当前配置:", + "yaml_control": "要以 YAML 模式下自行编辑,请创建 YAML 文件并命名为在配置中为此仪表盘指定的名称,或使用默认的 'ui-lovelace.yaml'。", + "yaml_mode": "您正在使用 YAML 模式,因此无法从 UI 更改 Lovelace 配置。如果要从 UI 更改 Lovelace,请从 'configuration.yaml' 中的 Lovelace 配置中删除 'mode:yaml'。" }, "suggest_card": { "add": "添加至 Lovelace UI", From 319a3b49430df96104ee7aee13f20ad9bb8d4ce9 Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Mon, 2 Mar 2020 00:36:43 +0000 Subject: [PATCH 05/36] [ci skip] Translation update --- translations/ca.json | 42 +++++++++++ translations/de.json | 76 +++++++++++++++++++- translations/hu.json | 2 +- translations/it.json | 148 +++++++++++++++++++++++++++++++++++++- translations/ko.json | 142 +++++++++++++++++++++++++++++++++++- translations/ru.json | 1 + translations/zh-Hant.json | 79 +++++++++++++++++++- 7 files changed, 482 insertions(+), 8 deletions(-) diff --git a/translations/ca.json b/translations/ca.json index 55041c8f2d..7c2b1af87b 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -1417,6 +1417,48 @@ "note_about_website_reference": "N'hi ha més disponibles al " }, "introduction": "Aquí pots configurar Home Assistant i els seus components. Encara no és possible configurar-ho tot des de la interfície d'usuari, però hi estem treballant.", + "lovelace": { + "dashboards": { + "detail": { + "create": "Crea", + "delete": "Suprimeix", + "require_admin": "Només administrador", + "update": "Actualitza", + "url": "URL" + } + }, + "resources": { + "caption": "Recursos", + "confirm_delete": "Estàs segur que vols eliminar aquest recurs?", + "detail": { + "create": "Crea", + "delete": "Suprimeix", + "dismiss": "Tanca", + "new_resource": "Afegeix nou recurs", + "type": "Tipus de recurs", + "update": "Actualitza", + "url": "URL", + "url_error_msg": "L'URL és un camp obligatori", + "warning_header": "Ves amb compte!", + "warning_text": "Afegir recursos pot ser perillós, assegura't de conèixer i confiar en el seu origen. Els recursos fraudulents poden perjudicar greument el teu sistema." + }, + "picker": { + "add_resource": "Afegeix recurs", + "headers": { + "type": "Tipus", + "url": "URL" + } + }, + "refresh_body": "Has d’actualitzar la pàgina per completar l’eliminació. Vols actualitzar-la ara?", + "refresh_header": "Vols actualitzar?", + "types": { + "css": "Fitxer d’estils", + "html": "HTML (obsolet)", + "js": "Fitxer JavaScript (obsolet)", + "module": "Mòdul JavaScript" + } + } + }, "person": { "add_person": "Afegeix persona", "caption": "Persones", diff --git a/translations/de.json b/translations/de.json index 01197c5afa..df39059a98 100644 --- a/translations/de.json +++ b/translations/de.json @@ -626,6 +626,8 @@ "enabled_description": "Deaktivierte Entitäten werden nicht zu Home Assistant hinzugefügt.", "enabled_label": "Entität aktivieren", "entity_id": "Entitäts-ID", + "icon": "Symbol Überschreiben", + "icon_error": "Symbole sollten das Format 'Präfix:iconname' haben, z. B. 'mdi:home'", "name": "Namen überschreiben", "note": "Hinweis: Dies funktioniert möglicherweise noch nicht bei allen Integrationen.", "unavailable": "Diese Entität ist derzeit nicht verfügbar.", @@ -637,6 +639,7 @@ }, "generic": { "cancel": "Abbrechen", + "close": "schließen", "default_confirmation_title": "Sind Sie sicher?", "ok": "OK" }, @@ -651,10 +654,12 @@ "has_time": "Zeit" }, "input_number": { + "box": "Eingabefeld", "max": "Maximaler Wert", "min": "Minimaler Wert", "mode": "Anzeigemodus", "slider": "Schieberegler", + "step": "Schrittweite des Schiebereglers", "unit_of_measurement": "Maßeinheit" }, "input_select": { @@ -1391,6 +1396,7 @@ "header": "Home Assistant konfigurieren", "helpers": { "caption": "Helfer", + "description": "Elemente, die beim Aufbau von Automatisierungen helfen können.", "dialog": { "add_helper": "Helfer hinzufügen", "add_platform": "{platform} hinzufügen", @@ -1408,6 +1414,7 @@ "input_boolean": "Umschalten", "input_datetime": "Datum und/oder Uhrzeit", "input_number": "Nummer", + "input_select": "Dropdown", "input_text": "Text" } }, @@ -1472,6 +1479,71 @@ "note_about_website_reference": "Weitere Informationen finden Sie auf der " }, "introduction": "Hier ist es möglich, deine Komponenten und Home Assistant zu konfigurieren. Noch ist nicht alles über die GUI einstellbar, aber wir arbeiten daran.", + "lovelace": { + "caption": "Lovelace Dashboards", + "dashboards": { + "caption": "Dashboards", + "conf_mode": { + "storage": "UI gesteuert", + "yaml": "YAML-Datei" + }, + "confirm_delete": "Sind Sie sicher, dass Sie dieses Dashboard löschen möchten?", + "detail": { + "create": "Erstellen", + "delete": "Löschen", + "dismiss": "Schließen", + "edit_dashboard": "Dashboard bearbeiten", + "icon": "Seitenleistensymbol", + "new_dashboard": "Neues Dashboard hinzufügen", + "require_admin": "Nur Admin", + "show_sidebar": "In der Seitenleiste anzeigen", + "title": "Titel der Seitenleiste", + "update": "Aktualisieren", + "url": "Url" + }, + "picker": { + "add_dashboard": "Dashboard hinzufügen", + "headers": { + "conf_mode": "Konfigurationsmethode", + "filename": "Dateiname", + "require_admin": "Nur für Administratoren", + "sidebar": "In der Seitenleiste anzeigen", + "title": "Titel" + }, + "open": "Dashboard öffnen" + } + }, + "description": "Konfigurieren Sie Ihre Lovelace-Dashboards", + "resources": { + "caption": "Ressourcen", + "confirm_delete": "Sind Sie sicher, dass Sie diese Ressource löschen möchten?", + "detail": { + "create": "Erstellen", + "delete": "Löschen", + "dismiss": "Schließen", + "new_resource": "Neue Ressource hinzufügen", + "type": "Ressource-Typ", + "update": "Aktualisieren", + "url": "Url", + "url_error_msg": "URL ist ein Pflichtfeld", + "warning_header": "Seien Sie vorsichtig!" + }, + "picker": { + "add_resource": "Ressource hinzufügen", + "headers": { + "type": "Typ", + "url": "Url" + } + }, + "refresh_header": "Möchten Sie aktualisieren?", + "types": { + "css": "Stylesheet", + "html": "HTML (veraltet)", + "js": "JavaScript-Datei (veraltet)", + "module": "JavaScript-Modul" + } + } + }, "person": { "add_person": "Person hinzufügen", "caption": "Personen", @@ -2171,10 +2243,12 @@ }, "save_config": { "cancel": "Abbrechen", + "close": "Schließen", "header": "Lovelace Userinterface selbst verwalten", "para": "Standardmäßig verwaltet Home Assistant Ihre Benutzeroberfläche und aktualisiert sie, sobald neue Entitäten oder Lovelace-Komponenten verfügbar sind. Wenn Sie die Verwaltung selbst übernehmen wollen, nehmen wir für Sie keine Änderungen mehr vor.", "para_sure": "Sind Sie sicher, dass Sie die Benutzeroberfläche selbst verwalten möchten?", - "save": "Kontrolle übernehmen" + "save": "Kontrolle übernehmen", + "yaml_config": "Um Ihnen den Einstieg zu erleichtern, finden Sie hier die aktuelle Konfiguration dieses Dashboards:" }, "suggest_card": { "add": "Zu Lovelace hinzufügen", diff --git a/translations/hu.json b/translations/hu.json index 05bd034ae9..fcbd409dcf 100644 --- a/translations/hu.json +++ b/translations/hu.json @@ -1918,7 +1918,7 @@ }, "safe-mode": { "description": "A Home Assistant a konfiguráció betöltése közben bajba került, és most csökkentett módban fut. Vessen egy pillantást a hibanaplóra, hogy lássa, mi romlott el.", - "header": "Biztonsági mód aktiválva", + "header": "Csökkentett mód aktiválva", "show_errors": "Hibák megjelenítése" }, "shopping-list": { diff --git a/translations/it.json b/translations/it.json index 11f7ba467a..f01f362f87 100644 --- a/translations/it.json +++ b/translations/it.json @@ -626,6 +626,8 @@ "enabled_description": "Le entità disabilitate non saranno aggiunte a Home Assistant", "enabled_label": "Abilita entità", "entity_id": "ID entità", + "icon": "Sostituzione icona", + "icon_error": "Le icone dovrebbero essere nel formato 'prefisso:nome_icona', ad esempio 'mdi:home'.", "name": "Sostituzione nome", "note": "Nota: questo potrebbe non funzionare ancora con tutte le integrazioni.", "unavailable": "Questa entità non è attualmente disponibile.", @@ -637,9 +639,48 @@ }, "generic": { "cancel": "Annulla", + "close": "Chiudi", "default_confirmation_title": "Sei sicuro?", "ok": "OK" }, + "helper_settings": { + "generic": { + "icon": "Icona", + "initial_value": "Valore iniziale all'avvio", + "initial_value_explain": "Il valore che l'elemento avrà all'avvio di Home Assistant. Quando viene lasciato vuoto, il valore viene ripristinato al valore precedente.", + "name": "Nome" + }, + "input_datetime": { + "has_date": "Data", + "has_time": "Ora" + }, + "input_number": { + "box": "Campo di immissione", + "max": "Valore massimo", + "min": "Valore minimo", + "mode": "Modalità di visualizzazione", + "slider": "Cursore", + "step": "Dimensione del passo del cursore", + "unit_of_measurement": "Unità di misura" + }, + "input_select": { + "add": "Aggiungi", + "add_option": "Aggiungi opzione", + "no_options": "Non ci sono ancora opzioni.", + "options": "Opzioni" + }, + "input_text": { + "max": "Lunghezza massima", + "min": "Lunghezza minima", + "mode": "Modalità di visualizzazione", + "password": "Password", + "pattern": "Schema Regex per la validazione lato cliente", + "text": "Testo" + }, + "not_editable": "Non modificabile", + "not_editable_text": "Questa entità non può essere modificata dall'Interfaccia Utente perché è definita in configuration.yaml.", + "required_error_msg": "Questo campo è obbligatorio" + }, "more_info_control": { "dismiss": "Chiudi finestra di dialogo", "edit": "Modifica entità", @@ -1356,6 +1397,30 @@ } }, "header": "Configura Home Assistant", + "helpers": { + "caption": "Aiutanti", + "description": "Elementi che possono aiutare a costruire le automazioni.", + "dialog": { + "add_helper": "Aggiungi aiuto", + "add_platform": "Aggiungi {platform}", + "create": "Crea" + }, + "picker": { + "add_helper": "Aggiungi aiuto", + "headers": { + "editable": "Modificabile", + "name": "Nome", + "type": "Tipo" + } + }, + "types": { + "input_boolean": "Commutatore", + "input_datetime": "Data e/o ora", + "input_number": "Numero", + "input_select": "A discesa", + "input_text": "Testo" + } + }, "integrations": { "caption": "Integrazioni", "config_entry": { @@ -1417,6 +1482,76 @@ "note_about_website_reference": "Ulteriori informazioni sono disponibili su " }, "introduction": "Qui è possibile configurare i componenti e Home Assistant. Non è ancora possibile configurare tutto dall'Interfaccia Utente, ma ci stiamo lavorando.", + "lovelace": { + "caption": "Cruscotti Lovelace", + "dashboards": { + "cant_edit_yaml": "I cruscotti definiti in YAML non possono essere modificati dall'Interfaccia Utente. Modificarli in configuration.yaml.", + "caption": "Cruscotti", + "conf_mode": { + "storage": "IU controllata", + "yaml": "File YAML" + }, + "confirm_delete": "Sei sicuro di voler eliminare questo cruscotto?", + "detail": { + "create": "Crea", + "delete": "Elimina", + "dismiss": "Chiudi", + "edit_dashboard": "Modifica cruscotto", + "icon": "Icona della barra laterale", + "new_dashboard": "Aggiungi nuovo cruscotto", + "require_admin": "Solo per l'amministratore", + "show_sidebar": "Mostra nella barra laterale", + "title": "Titolo della barra laterale", + "update": "Aggiorna", + "url": "Url", + "url_error_msg": "L'URL non può contenere spazi o caratteri speciali, ad eccezione di _ e -" + }, + "picker": { + "add_dashboard": "Aggiungi cruscotto", + "headers": { + "conf_mode": "Metodo di configurazione", + "filename": "Nome del file", + "require_admin": "Solo per l'amministratore", + "sidebar": "Mostra nella barra laterale", + "title": "Titolo" + }, + "open": "Apri cruscotto" + } + }, + "description": "Configurare i cruscotti Lovelace", + "resources": { + "cant_edit_yaml": "Si utilizza Lovelace in modalità YAML, pertanto non è possibile gestire le risorse tramite l'Interfaccia Utente. Gestirli in configuration.yaml.", + "caption": "Risorse", + "confirm_delete": "Sei sicuro di voler eliminare questa risorsa?", + "detail": { + "create": "Crea", + "delete": "Elimina", + "dismiss": "Chiudi", + "new_resource": "Aggiungi nuova risorsa", + "type": "Tipo di risorsa", + "update": "Aggiorna", + "url": "Url", + "url_error_msg": "Url è un campo obbligatorio", + "warning_header": "Siate cauti!", + "warning_text": "L'aggiunta di risorse può essere pericolosa, assicurarsi di conoscere l'origine della risorsa e considerarle attendibili. Le risorse sbagliate potrebbero danneggiare seriamente il sistema." + }, + "picker": { + "add_resource": "Aggiungi risorsa", + "headers": { + "type": "Tipo", + "url": "Url" + } + }, + "refresh_body": "Devi aggiornare la pagina per completare la rimozione, vuoi aggiornare ora?", + "refresh_header": "Vuoi aggiornare?", + "types": { + "css": "Foglio di stile", + "html": "HTML (obsoleto)", + "js": "File JavaScript (obsoleto)", + "module": "Modulo JavaScript" + } + } + }, "person": { "add_person": "Aggiungi persona", "caption": "Persone", @@ -1928,7 +2063,7 @@ } }, "changed_toast": { - "message": "La configurazione dell'Interfaccia Utente di Lovelace è stata aggiornata, ricaricare per visualizzare i cambiamenti?", + "message": "La configurazione dell'Interfaccia Utente di Lovelace per questo cruscotto è stata aggiornata, ricaricare per vedere le modifiche?", "refresh": "Aggiorna" }, "editor": { @@ -2084,7 +2219,9 @@ "header": "Visualizza configurazione", "header_name": "{name} Visualizza configurazione", "move_left": "Sposta la vista a sinistra", - "move_right": "Sposta la vista a destra" + "move_right": "Sposta la vista a destra", + "tab_badges": "Distintivi", + "tab_settings": "Impostazioni" }, "header": "Modifica dell'interfaccia utente", "menu": { @@ -2107,16 +2244,21 @@ "error_remove": "Impossibile rimuovere la configurazione: {error}", "error_save_yaml": "Impossibile salvare YAML: {error}", "header": "Modifica Configurazione", + "resources_moved": "Le risorse non dovrebbero più essere aggiunte alla configurazione di Lovelace ma possono essere aggiunte nel pannello di configurazione di Lovelace.", "save": "Salva", "saved": "Salvato", "unsaved_changes": "Modifiche non salvate" }, "save_config": { "cancel": "Rinuncia", + "close": "Chiudi", "header": "Prendi il controllo della tua interfaccia utente di Lovelace", "para": "Per impostazione predefinita, Home Assistant gestirà l'Interfaccia Utente, aggiornandola quando nuove entità o componenti dell'IU Lovelace diventano disponibili. Se prendi il controllo non effettueremo più automaticamente le modifiche per te.", "para_sure": "Sei sicuro di voler prendere il controllo della tua interfaccia utente?", - "save": "Prendere il controllo" + "save": "Prendere il controllo", + "yaml_config": "Per aiutarti a iniziare qui c'è la configurazione corrente di questo cruscotto:", + "yaml_control": "Per assumere il controllo in modalità YAML, creare un file YAML con il nome specificato nella configurazione per questo cruscotto, o il predefinito 'ui-lovelace.yaml'.", + "yaml_mode": "Si sta utilizzando la modalità YAML, il che significa che non è possibile modificare la configurazione Lovelace dall'Interfaccia Utente. Se si desidera modificare Lovelace dall'Interfaccia Utente, rimuovere la riga con 'mode: yaml' dalla configurazione Lovelace in 'configuration.yaml'." }, "suggest_card": { "add": "Aggiungi all'interfaccia utente di Lovelace", diff --git a/translations/ko.json b/translations/ko.json index 8c04b0443e..e04c64148b 100644 --- a/translations/ko.json +++ b/translations/ko.json @@ -626,6 +626,8 @@ "enabled_description": "비활성화 된 구성요소는 Home Assistant 에 추가되지 않습니다.", "enabled_label": "구성요소 활성화", "entity_id": "구성요소 ID", + "icon": "아이콘 재정의(Override)", + "icon_error": "아이콘을 지정할 때 'mdi:home'과 같이 '접두사:아이콘' 형식이어야 합니다.", "name": "대체 이름", "note": "참고: 아직 모든 통합 구성요소에 적용되지 않을 수 있습니다.", "unavailable": "이 구성요소는 현재 사용할 수 없습니다.", @@ -637,9 +639,48 @@ }, "generic": { "cancel": "취소", + "close": "닫기", "default_confirmation_title": "다시 한번 확인해주세요", "ok": "확인" }, + "helper_settings": { + "generic": { + "icon": "아이콘", + "initial_value": "시작 시 초기값", + "initial_value_explain": "Home Assistant가 켜질 때 요소가 가지는 초기값입니다. 비워두면 요소의 원래 값으로 복원됩니다.", + "name": "이름" + }, + "input_datetime": { + "has_date": "날짜", + "has_time": "시간" + }, + "input_number": { + "box": "입력란", + "max": "최대값", + "min": "최소값", + "mode": "디스플레이 모드", + "slider": "슬라이더", + "step": "슬라이더의 간격 크기", + "unit_of_measurement": "단위" + }, + "input_select": { + "add": "추가", + "add_option": "옵션 추가", + "no_options": "아직 옵션이 없습니다.", + "options": "옵션" + }, + "input_text": { + "max": "최대 길이", + "min": "최대 길이", + "mode": "디스플레이 모드", + "password": "비밀번호", + "pattern": "클라이언트 측 검증을 위한 정규표현식(Regex)", + "text": "텍스트" + }, + "not_editable": "수정 불가능", + "not_editable_text": "이 구성요소는 configuration.yaml에서 정의되었기 때문에 UI에서 수정할 수 없습니다.", + "required_error_msg": "이 입력란은 필수 요소입니다" + }, "more_info_control": { "dismiss": "대화창 닫기", "edit": "구성요소 편집", @@ -1356,6 +1397,30 @@ } }, "header": "Home Assistant 설정", + "helpers": { + "caption": "도우미", + "description": "자동화를 구축하는 데 도움이 되는 요소입니다.", + "dialog": { + "add_helper": "도우미 추가", + "add_platform": "{platform} 추가...", + "create": "만들기" + }, + "picker": { + "add_helper": "도우미 추가", + "headers": { + "editable": "수정 가능", + "name": "이름", + "type": "종류" + } + }, + "types": { + "input_boolean": "토글", + "input_datetime": "날짜 또는 시간", + "input_number": "수", + "input_select": "드롭다운", + "input_text": "텍스트" + } + }, "integrations": { "caption": "통합 구성요소", "config_entry": { @@ -1417,6 +1482,76 @@ "note_about_website_reference": "더 많은 구성요소는 다음에서 살펴 봐주세요. " }, "introduction": "여기에서 구성요소와 Home Assistant 를 설정 할 수 있습니다. 아직 여기서 모두 설정 할 수는 없지만, 모든 내용을 설정 할 수 있도록 작업 중입니다.", + "lovelace": { + "caption": "Lovelace 대시보드", + "dashboards": { + "cant_edit_yaml": "YAML에 정의된 대시보드는 UI에서 편집할 수 없습니다. configuration.yaml 파일에서 수정하십시오.", + "caption": "대시보드", + "conf_mode": { + "storage": "UI 제어", + "yaml": "YAML 파일" + }, + "confirm_delete": "이 대시보드를 삭제하시겠습니까?", + "detail": { + "create": "만들기", + "delete": "삭제", + "dismiss": "닫기", + "edit_dashboard": "대시보드 편집", + "icon": "사이드바 아이콘", + "new_dashboard": "새로운 대시보드 추가...", + "require_admin": "관리자 전용", + "show_sidebar": "사이드바에서 표시", + "title": "사이드바 제목", + "update": "업데이트", + "url": "URL", + "url_error_msg": "URL은 _와 -를 제외한 공백이나 특수 문자를 포함할 수 없습니다." + }, + "picker": { + "add_dashboard": "대시보드 추가", + "headers": { + "conf_mode": "구성 방법", + "filename": "파일 이름", + "require_admin": "관리자 전용", + "sidebar": "사이드바에서 표시", + "title": "제목" + }, + "open": "대시보드 열기" + } + }, + "description": "Lovelace 대시보드 구성", + "resources": { + "cant_edit_yaml": "당신은 Lovelace를 YAML 모드로 사용 중이므로 리소스들을 UI에서 관리할 수 없습니다. configuration.yaml에서 관리하십시오.", + "caption": "리소스", + "confirm_delete": "이 리소스롤 삭제하시겠습니까?", + "detail": { + "create": "만들기", + "delete": "삭제", + "dismiss": "닫기", + "new_resource": "새로운 리소스 추가...", + "type": "리소스 유형", + "update": "업데이트", + "url": "URL", + "url_error_msg": "URL은 필수 입력 입니다", + "warning_header": "주의하세요!", + "warning_text": "리소스를 추가하는 것은 위험할 수 있으므로 신뢰할 수 있는 출처인지 확인하시기 바랍니다. 부적합한 리소스는 시스템에 심각한 손상을 가할 수 있습니다." + }, + "picker": { + "add_resource": "새로운 리소스 추가...", + "headers": { + "type": "종류", + "url": "URL" + } + }, + "refresh_body": "삭제를 완료하려면 페이지를 새로 고쳐야야합니다. 지금 새로 고치시겠습니까?", + "refresh_header": "새로고침을 하시겠습니까?", + "types": { + "css": "스타일시트", + "html": "HTML (권장하지 않음)", + "js": "자바스크립트 파일 (권장하지 않음)", + "module": "자바스크립트 모듈" + } + } + }, "person": { "add_person": "구성원 추가하기", "caption": "구성원", @@ -2109,16 +2244,21 @@ "error_remove": "구성을 제거할 수 없습니다: {error}", "error_save_yaml": "YAML 을 저장할 수 없습니다: {error}", "header": "구성 코드 편집", + "resources_moved": "리소스는 더이상 Lovelace 구성에 추가하지 말아야 하지만 Lovelace 구성 패널에서 추가할 수 있습니다.", "save": "저장하기", "saved": "저장되었습니다", "unsaved_changes": "저장되지 않은 변경사항" }, "save_config": { "cancel": "아닙니다", + "close": "닫기", "header": "Lovelace UI 직접 관리하기", "para": "기본적으로 Home Assistant 는 사용자 인터페이스를 유지 관리하고, 사용할 수 있는 새로운 구성요소 또는 Lovelace 구성요소가 있을 때 업데이트를 합니다. 사용자가 직접 관리하는 경우 Home Assistant 는 더 이상 자동으로 변경하지 않습니다.", "para_sure": "사용자 인터페이스를 직접 관리하시겠습니까?", - "save": "직접 관리할게요" + "save": "직접 관리할게요", + "yaml_config": "대시보드의 현재 설정은 다음과 같습니다.", + "yaml_control": "YAML 모드로 제어하려면 대시보드의 구성에서 지정한 이름 또는 'ui-lovelace.yaml'(기본값)으로 YAML 파일을 만드십시오.", + "yaml_mode": "YAML 모드를 사용 중이므로 UI에서 Lovelace 구성을 변경할 수 없습니다. UI에서 Lovelace를 변경하려면 'configuration.yaml'의 Lovelace 구성에서 'mode : yaml'을 제거하십시오." }, "suggest_card": { "add": "Lovelace UI 에 추가", diff --git a/translations/ru.json b/translations/ru.json index 627c39151d..107f4aff6a 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -2247,6 +2247,7 @@ "para": "По умолчанию Home Assistant будет обслуживать Ваш пользовательский интерфейс, автоматически добавляя новые объекты и новые компоненты Lovelace, если они доступны. Если Вы получите контроль над пользовательским интерфейсом, изменения больше не будут вноситься автоматически.", "para_sure": "Вы уверены, что хотите самостоятельно контролировать пользовательский интерфейс?", "save": "Получить контроль", + "yaml_config": "Вы можете использовать текущую конфигурацию этой панели как основу для редактирования:", "yaml_control": "Чтобы получить контроль в режиме YAML, создайте файл с именем, указанным в настройках этой панели (по умолчанию 'ui-lovelace.yaml').", "yaml_mode": "Вы не можете изменять конфигурацию Lovelace из пользовательского интерфейса, так как он используется в режиме YAML. Если Вы всё же хотите редактировать Lovelace из пользовательского интерфейса, удалите строку 'mode: yaml' в файле 'configuration.yaml'." }, diff --git a/translations/zh-Hant.json b/translations/zh-Hant.json index 10e2377b1e..87c07f07bd 100644 --- a/translations/zh-Hant.json +++ b/translations/zh-Hant.json @@ -639,6 +639,7 @@ }, "generic": { "cancel": "取消", + "close": "關閉", "default_confirmation_title": "確認?", "ok": "好" }, @@ -1481,6 +1482,76 @@ "note_about_website_reference": "更多資訊請參閱" }, "introduction": "此處為 Home Assistant 和元件相關配置區,目前尚未支援透過 UI 進行所有設定,我們正在努力改進中。", + "lovelace": { + "caption": "Lovelace 主面板", + "dashboards": { + "cant_edit_yaml": "於 YAML 中定義的主面板無法藉由 UI 編輯,請於 Configuration.yaml 進行更改。", + "caption": "主面板", + "conf_mode": { + "storage": "UI 已控制", + "yaml": "YAML 檔案" + }, + "confirm_delete": "確定要刪除此主面板?", + "detail": { + "create": "新增", + "delete": "刪除", + "dismiss": "關閉", + "edit_dashboard": "編輯主面板", + "icon": "側邊欄圖示", + "new_dashboard": "新增主面板", + "require_admin": "僅限管理員", + "show_sidebar": "於側邊列顯示", + "title": "側邊欄標題", + "update": "更新", + "url": "網址", + "url_error_msg": "網址不能包含空格或特殊字元,除了 _ 及 -。" + }, + "picker": { + "add_dashboard": "新增主面板", + "headers": { + "conf_mode": "設定模式", + "filename": "檔名", + "require_admin": "僅限管理員", + "sidebar": "於側邊列顯示", + "title": "標題" + }, + "open": "開啟主面板" + } + }, + "description": "設定 Lovelace 主面板", + "resources": { + "cant_edit_yaml": "正使用 YAML 模式、因此無法藉由 UI 變更 Lovelace 設定。請於「configuration.yaml」進行管理。", + "caption": "資源", + "confirm_delete": "確定要刪除此資源?", + "detail": { + "create": "新增", + "delete": "刪除", + "dismiss": "關閉", + "new_resource": "新增資源", + "type": "資源類型", + "update": "更新", + "url": "網址", + "url_error_msg": "網址為必填欄位", + "warning_header": "請特別注意!", + "warning_text": "新增資源具有風險性、請確認資源來源安全性。惡意的資源可能嚴重損害您的系統。" + }, + "picker": { + "add_resource": "新增資源", + "headers": { + "type": "類別", + "url": "網址" + } + }, + "refresh_body": "必須更新頁面以完成移除、要立即更新?", + "refresh_header": "是否要更新?", + "types": { + "css": "Stylesheet", + "html": "HTML(已棄用)", + "js": "JavaScript 檔案(已棄用)", + "module": "JavaScript 模組" + } + } + }, "person": { "add_person": "新增人員", "caption": "人員", @@ -1992,7 +2063,7 @@ } }, "changed_toast": { - "message": "此 Lovelace UI 設定已更新,是否要更新頁面檢視變更?", + "message": "此主面板 Lovelace UI 設定已更新,是否要更新頁面檢視變更?", "refresh": "更新" }, "editor": { @@ -2180,10 +2251,14 @@ }, "save_config": { "cancel": "我再想想", + "close": "關閉", "header": "自行編輯 Lovelace UI", "para": "Home Assistant 於預設下,將維護您的使用者介面、於新物件或 Lovelace UI 元件可使用時進行更新。假如選擇自行編輯,系統將不再為您自動進行變更。", "para_sure": "確定要自行編輯使用者介面?", - "save": "自行編輯" + "save": "自行編輯", + "yaml_config": "目前此主面板設定:", + "yaml_control": "欲使用 YAML 進行控制、新增一個與此主面板相同名稱的 YAML 檔案、或者預設為「ui-lovelace.yaml」。", + "yaml_mode": "正使用 YAML 模式、表示您無法藉由 UI 變更 Lovelace 設定。假如您想由 UI 中進行變更、請於「configuration.yaml」檔案中移除「mode: yaml」參數。" }, "suggest_card": { "add": "新增至 Lovelace UI", From d74fe6ed522d76f4889bcd1ff8836d4a2fa0cd61 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 2 Mar 2020 02:36:00 -0800 Subject: [PATCH 06/36] Update translation hashing (#5025) * Update translation hashing * Move gulp-rename --- build-scripts/gulp/app.js | 2 +- build-scripts/gulp/gen-icons.js | 15 +- build-scripts/gulp/translations.js | 458 ++++++++++------------ build-scripts/util.js | 16 + build-scripts/webpack.js | 14 +- package.json | 6 +- src/translations/translationMetadata.json | 2 +- src/types.ts | 2 +- src/util/hass-translation.ts | 7 +- yarn.lock | 148 +------ 10 files changed, 274 insertions(+), 396 deletions(-) create mode 100644 build-scripts/util.js diff --git a/build-scripts/gulp/app.js b/build-scripts/gulp/app.js index 985067c71f..9fdf96cc04 100644 --- a/build-scripts/gulp/app.js +++ b/build-scripts/gulp/app.js @@ -24,7 +24,7 @@ gulp.task( gulp.parallel("gen-icons-app", "gen-icons-mdi"), "gen-pages-dev", "gen-index-app-dev", - gulp.series("create-test-translation", "build-translations") + "build-translations" ), "copy-static", "webpack-watch-app" diff --git a/build-scripts/gulp/gen-icons.js b/build-scripts/gulp/gen-icons.js index b116b8abea..8ce9f7612b 100644 --- a/build-scripts/gulp/gen-icons.js +++ b/build-scripts/gulp/gen-icons.js @@ -2,6 +2,7 @@ const gulp = require("gulp"); const path = require("path"); const fs = require("fs"); const paths = require("../paths"); +const { mapFiles } = require("../util"); const ICON_PACKAGE_PATH = path.resolve( __dirname, @@ -57,20 +58,6 @@ function generateIconset(iconsetName, iconNames) { return `${iconDefs}`; } -// Helper function to map recursively over files in a folder and it's subfolders -function mapFiles(startPath, filter, mapFunc) { - const files = fs.readdirSync(startPath); - for (let i = 0; i < files.length; i++) { - const filename = path.join(startPath, files[i]); - const stat = fs.lstatSync(filename); - if (stat.isDirectory()) { - mapFiles(filename, filter, mapFunc); - } else if (filename.indexOf(filter) >= 0) { - mapFunc(filename); - } - } -} - // Find all icons used by the project. function findIcons(searchPath, iconsetName) { const iconRegex = new RegExp(`${iconsetName}:[\\w-]+`, "g"); diff --git a/build-scripts/gulp/translations.js b/build-scripts/gulp/translations.js index a484a8f32b..80e2e72213 100755 --- a/build-scripts/gulp/translations.js +++ b/build-scripts/gulp/translations.js @@ -1,14 +1,17 @@ +const crypto = require("crypto"); const del = require("del"); const path = require("path"); +const source = require("vinyl-source-stream"); +const vinylBuffer = require("vinyl-buffer"); const gulp = require("gulp"); const fs = require("fs"); const foreach = require("gulp-foreach"); -const hash = require("gulp-hash"); -const hashFilename = require("gulp-hash-filename"); const merge = require("gulp-merge-json"); const minify = require("gulp-jsonminify"); const rename = require("gulp-rename"); const transform = require("gulp-json-transform"); +const { mapFiles } = require("../util"); +const env = require("../env"); const inDir = "translations"; const workDir = "build-translations"; @@ -39,8 +42,6 @@ const TRANSLATION_FRAGMENTS = [ "developer-tools", ]; -const tasks = []; - function recursiveFlatten(prefix, data) { let output = {}; Object.keys(data).forEach(function(key) { @@ -116,11 +117,9 @@ function lokaliseTransform(data, original, file) { return output; } -let taskName = "clean-translations"; -gulp.task(taskName, function() { - return del([`${outDir}/**/*.json`]); +gulp.task("clean-translations", function() { + return del([workDir]); }); -tasks.push(taskName); gulp.task("ensure-translations-build-dir", (done) => { if (!fs.existsSync(workDir)) { @@ -129,27 +128,21 @@ gulp.task("ensure-translations-build-dir", (done) => { done(); }); -taskName = "create-test-metadata"; -gulp.task( - taskName, - gulp.series("ensure-translations-build-dir", function writeTestMetaData(cb) { - fs.writeFile( - workDir + "/testMetadata.json", - JSON.stringify({ - test: { - nativeName: "Test", - }, - }), - cb - ); - }) -); -tasks.push(taskName); +gulp.task("create-test-metadata", function(cb) { + fs.writeFile( + workDir + "/testMetadata.json", + JSON.stringify({ + test: { + nativeName: "Test", + }, + }), + cb + ); +}); -taskName = "create-test-translation"; gulp.task( - taskName, - gulp.series("create-test-metadata", function() { + "create-test-translation", + gulp.series("create-test-metadata", function createTestTranslation() { return gulp .src("src/translations/en.json") .pipe( @@ -161,7 +154,6 @@ gulp.task( .pipe(gulp.dest(workDir)); }) ); -tasks.push(taskName); /** * This task will build a master translation file, to be used as the base for @@ -172,235 +164,215 @@ tasks.push(taskName); * project is buildable immediately after merging new translation keys, since * the Lokalise update to translations/en.json will not happen immediately. */ -taskName = "build-master-translation"; -gulp.task( - taskName, - gulp.series("clean-translations", function() { - return gulp - .src("src/translations/en.json") - .pipe( - transform(function(data, file) { - return lokaliseTransform(data, data, file); - }) - ) - .pipe(rename("translationMaster.json")) - .pipe(gulp.dest(workDir)); - }) -); -tasks.push(taskName); +gulp.task("build-master-translation", function() { + return gulp + .src("src/translations/en.json") + .pipe( + transform(function(data, file) { + return lokaliseTransform(data, data, file); + }) + ) + .pipe(rename("translationMaster.json")) + .pipe(gulp.dest(workDir)); +}); -taskName = "build-merged-translations"; -gulp.task( - taskName, - gulp.series("build-master-translation", function() { - return gulp - .src([inDir + "/*.json", workDir + "/test.json"], { allowEmpty: true }) - .pipe( - transform(function(data, file) { - return lokaliseTransform(data, data, file); - }) - ) - .pipe( - foreach(function(stream, file) { - // For each language generate a merged json file. It begins with the master - // translation as a failsafe for untranslated strings, and merges all parent - // tags into one file for each specific subtag - // - // TODO: This is a naive interpretation of BCP47 that should be improved. - // Will be OK for now as long as we don't have anything more complicated - // than a base translation + region. - const tr = path.basename(file.history[0], ".json"); - const subtags = tr.split("-"); - const src = [workDir + "/translationMaster.json"]; - for (let i = 1; i <= subtags.length; i++) { - const lang = subtags.slice(0, i).join("-"); - if (lang === "test") { - src.push(workDir + "/test.json"); - } else if (lang !== "en") { - src.push(inDir + "/" + lang + ".json"); - } +gulp.task("build-merged-translations", function() { + return gulp + .src([inDir + "/*.json", workDir + "/test.json"], { allowEmpty: true }) + .pipe( + transform(function(data, file) { + return lokaliseTransform(data, data, file); + }) + ) + .pipe( + foreach(function(stream, file) { + // For each language generate a merged json file. It begins with the master + // translation as a failsafe for untranslated strings, and merges all parent + // tags into one file for each specific subtag + // + // TODO: This is a naive interpretation of BCP47 that should be improved. + // Will be OK for now as long as we don't have anything more complicated + // than a base translation + region. + const tr = path.basename(file.history[0], ".json"); + const subtags = tr.split("-"); + const src = [workDir + "/translationMaster.json"]; + for (let i = 1; i <= subtags.length; i++) { + const lang = subtags.slice(0, i).join("-"); + if (lang === "test") { + src.push(workDir + "/test.json"); + } else if (lang !== "en") { + src.push(inDir + "/" + lang + ".json"); } - return gulp - .src(src, { allowEmpty: true }) - .pipe(transform((data) => emptyFilter(data))) - .pipe( - merge({ - fileName: tr + ".json", - }) - ) - .pipe(gulp.dest(fullDir)); - }) - ); - }) -); -tasks.push(taskName); + } + return gulp + .src(src, { allowEmpty: true }) + .pipe(transform((data) => emptyFilter(data))) + .pipe( + merge({ + fileName: tr + ".json", + }) + ) + .pipe(gulp.dest(fullDir)); + }) + ); +}); + +var taskName; const splitTasks = []; TRANSLATION_FRAGMENTS.forEach((fragment) => { taskName = "build-translation-fragment-" + fragment; - gulp.task( - taskName, - gulp.series("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], - }, + gulp.task(taskName, 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); + }, + })) + ) + .pipe(gulp.dest(workDir + "/" + fragment)); + }); splitTasks.push(taskName); }); taskName = "build-translation-core"; -gulp.task( - taskName, - gulp.series("build-merged-translations", function() { - // 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); +gulp.task(taskName, function() { + // 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)); +}); + splitTasks.push(taskName); -taskName = "build-flattened-translations"; -gulp.task( - taskName, - gulp.series(...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(hashFilename()) - .pipe( - rename((filePath) => { - if (filePath.dirname === "core") { - filePath.dirname = ""; - } - }) - ) - .pipe(gulp.dest(outDir)); - }) -); -tasks.push(taskName); +gulp.task("build-flattened-translations", 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)); +}); -taskName = "build-translation-fingerprints"; -gulp.task( - taskName, - gulp.series("build-flattened-translations", function() { - return gulp - .src(outDir + "/**/*.json") - .pipe( - rename({ - extname: "", - }) - ) - .pipe( - hash({ - algorithm: "md5", - hashLength: 32, - template: "<%= name %>.json", - }) - ) - .pipe(hash.manifest("translationFingerprints.json")) - .pipe( - transform(function(data) { - // 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 [path, _md5] = key.rsplit("-", 1); - // let translation = key; - let translation = path; - const parts = translation.split("/"); - if (parts.length === 2) { - translation = parts[1]; - } - if (!(translation in newData)) { - newData[translation] = { - fingerprints: {}, - }; - } - newData[translation].fingerprints[path] = value; - }); - return newData; - }) - ) - .pipe(gulp.dest(workDir)); - }) -); -tasks.push(taskName); +const fingerprints = {}; -taskName = "build-translations"; gulp.task( - taskName, - gulp.series("build-translation-fingerprints", function() { - return gulp - .src( - [ - "src/translations/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 (data[key].nativeName) { - newData[key] = data[key]; - } else { - console.warn( - `Skipping language ${key}. Native name was not translated.` - ); - } - if (data[key]) newData[key] = value; - }); - return newData; - }) - ) - .pipe( - transform((data) => ({ - fragments: TRANSLATION_FRAGMENTS, - translations: data, - })) - ) - .pipe(rename("translationMetadata.json")) - .pipe(gulp.dest(workDir)); - }) -); -tasks.push(taskName); + "build-translation-fingerprints", + function fingerprintTranslationFiles() { + // Fingerprint full file of each language + const files = fs.readdirSync(fullDir); + for (let i = 0; i < files.length; i++) { + fingerprints[files[i].split(".")[0]] = { + // In dev we create fake hashes + hash: env.isProdBuild + ? crypto + .createHash("md5") + .update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8")) + .digest("hex") + : "dev", + }; + } -module.exports = tasks; + mapFiles(outDir, ".json", (filename) => { + const parsed = path.parse(filename); + + // nl.json -> nl-.json + if (!(parsed.name in fingerprints)) { + throw new Error(`Unable to find hash for ${filename}`); + } + + fs.renameSync( + filename, + `${parsed.dir}/${parsed.name}-${fingerprints[parsed.name].hash}${ + parsed.ext + }` + ); + }); + + const stream = source("translationFingerprints.json"); + stream.write(JSON.stringify(fingerprints)); + process.nextTick(() => stream.end()); + return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir)); + } +); + +gulp.task( + "build-translations", + gulp.series( + "clean-translations", + "ensure-translations-build-dir", + env.isProdBuild ? (done) => done() : "create-test-translation", + "build-master-translation", + "build-merged-translations", + gulp.parallel(...splitTasks), + "build-flattened-translations", + "build-translation-fingerprints", + function writeMetadata() { + return gulp + .src( + [ + "src/translations/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 (data[key].nativeName) { + newData[key] = data[key]; + } else { + console.warn( + `Skipping language ${key}. Native name was not translated.` + ); + } + if (data[key]) newData[key] = value; + }); + return newData; + }) + ) + .pipe( + transform((data) => ({ + fragments: TRANSLATION_FRAGMENTS, + translations: data, + })) + ) + .pipe(rename("translationMetadata.json")) + .pipe(gulp.dest(workDir)); + } + ) +); diff --git a/build-scripts/util.js b/build-scripts/util.js new file mode 100644 index 0000000000..23efdfb229 --- /dev/null +++ b/build-scripts/util.js @@ -0,0 +1,16 @@ +const path = require("path"); +const fs = require("fs"); + +// Helper function to map recursively over files in a folder and it's subfolders +module.exports.mapFiles = function mapFiles(startPath, filter, mapFunc) { + const files = fs.readdirSync(startPath); + for (let i = 0; i < files.length; i++) { + const filename = path.join(startPath, files[i]); + const stat = fs.lstatSync(filename); + if (stat.isDirectory()) { + mapFiles(filename, filter, mapFunc); + } else if (filename.indexOf(filter) >= 0) { + mapFunc(filename); + } + } +}; diff --git a/build-scripts/webpack.js b/build-scripts/webpack.js index 042d978461..88ad6ac1dc 100644 --- a/build-scripts/webpack.js +++ b/build-scripts/webpack.js @@ -148,11 +148,17 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => { // Create an object mapping browser urls to their paths during build const translationMetadata = require("../build-translations/translationMetadata.json"); const workBoxTranslationsTemplatedURLs = {}; - const englishFP = translationMetadata.translations.en.fingerprints; - Object.keys(englishFP).forEach((key) => { + const englishFilename = `en-${translationMetadata.translations.en.hash}.json`; + + // core + workBoxTranslationsTemplatedURLs[ + `/static/translations/${englishFilename}` + ] = `build-translations/output/${englishFilename}`; + + Object.keys(translationMetadata.fragments).forEach((fragment) => { workBoxTranslationsTemplatedURLs[ - `/static/translations/${englishFP[key]}` - ] = `build-translations/output/${key}.json`; + `/static/translations/${fragment}/${englishFilename}` + ] = `build-translations/output/${fragment}/${englishFilename}`; }); config.plugins.push( diff --git a/package.json b/package.json index 09facfc322..9c1c133861 100644 --- a/package.json +++ b/package.json @@ -145,13 +145,11 @@ "fs-extra": "^7.0.1", "gulp": "^4.0.0", "gulp-foreach": "^0.1.0", - "gulp-hash": "^4.2.2", - "gulp-hash-filename": "^2.0.1", "gulp-insert": "^0.5.0", "gulp-json-transform": "^0.4.6", "gulp-jsonminify": "^1.1.0", "gulp-merge-json": "^1.3.1", - "gulp-rename": "^1.4.0", + "gulp-rename": "^2.0.0", "gulp-zopfli-green": "^3.0.1", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", @@ -174,6 +172,8 @@ "tslint-eslint-rules": "^5.4.0", "tslint-plugin-prettier": "^2.0.1", "typescript": "^3.7.2", + "vinyl-buffer": "^1.0.1", + "vinyl-source-stream": "^2.0.0", "web-component-tester": "^6.9.2", "webpack": "^4.40.2", "webpack-cli": "^3.3.9", diff --git a/src/translations/translationMetadata.json b/src/translations/translationMetadata.json index 83f61b012d..908a8fb577 100644 --- a/src/translations/translationMetadata.json +++ b/src/translations/translationMetadata.json @@ -34,7 +34,7 @@ "nativeName": "English" }, "eo": { - "navtiveName": "Esperanto" + "nativeName": "Esperanto" }, "es": { "nativeName": "Español" diff --git a/src/types.ts b/src/types.ts index 13e7a358fd..90ba298389 100644 --- a/src/types.ts +++ b/src/types.ts @@ -95,7 +95,7 @@ export interface Panels { export interface Translation { nativeName: string; isRTL: boolean; - fingerprints: { [fragment: string]: string }; + hash: string; } export interface TranslationMetadata { diff --git a/src/util/hass-translation.ts b/src/util/hass-translation.ts index a792de7362..5378428402 100644 --- a/src/util/hass-translation.ts +++ b/src/util/hass-translation.ts @@ -122,8 +122,11 @@ export async function getTranslation( } throw new Error("Language en is not found in metadata"); } - const fingerprint = - metadata.fingerprints[fragment ? `${fragment}/${language}` : language]; + + // nl-abcd.jon or logbook/nl-abcd.json + const fingerprint = `${fragment ? fragment + "/" : ""}${language}-${ + metadata.hash + }.json`; // Fetch translation from the server if (!translations[fingerprint]) { diff --git a/yarn.lock b/yarn.lock index 4ca792c516..d2d859218e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3968,7 +3968,7 @@ binary-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== -bl@^1.0.0: +bl@^1.0.0, bl@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" integrity sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA== @@ -5951,11 +5951,6 @@ es6-object-assign@^1.1.0: resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= -es6-promise@^2.1.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-2.3.0.tgz#96edb9f2fdb01995822b263dd8aadab6748181bc" - integrity sha1-lu258v2wGZWCKyY92KratnSBgbw= - es6-promise@^4.0.3, es6-promise@^4.0.5: version "4.2.6" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f" @@ -7261,22 +7256,6 @@ gulp-foreach@^0.1.0: gulp-util "~2.2.14" through2 "~0.6.3" -gulp-hash-filename@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/gulp-hash-filename/-/gulp-hash-filename-2.0.1.tgz#c30656261a9b622d636766e48b8297125b4ddde8" - integrity sha512-pMg5owb8Dt0wqjgPx/TFbU3c5ckD16rrgo0BTm9PQ3pVC1Zsgw7AYx1+DP2t31JoUTeN1/dPuXNWnCNvN/wj7A== - -gulp-hash@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/gulp-hash/-/gulp-hash-4.2.2.tgz#2cf4ad081ef7a65393a51e3df58f514f388f4523" - integrity sha512-uWCjiy7ZXCPu4aaTM454+ImCnrR+07MKdwpunDZU0I7oUd5+SfyDxxhYzgpzSRVSlCFWfBBqNY9vAL3m3F+3uw== - dependencies: - es6-promise "^2.1.1" - lodash.assign "^2.4.1" - lodash.template "^2.4.1" - through2 "^2.0.0" - vinyl "^2.1.0" - gulp-if@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/gulp-if/-/gulp-if-2.0.2.tgz#a497b7e7573005041caa2bc8b7dda3c80444d629" @@ -7334,10 +7313,10 @@ gulp-merge-json@^1.3.1: through "^2.3.8" vinyl "^2.1.0" -gulp-rename@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/gulp-rename/-/gulp-rename-1.4.0.tgz#de1c718e7c4095ae861f7296ef4f3248648240bd" - integrity sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg== +gulp-rename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/gulp-rename/-/gulp-rename-2.0.0.tgz#9bbc3962b0c0f52fc67cd5eaff6c223ec5b9cf6c" + integrity sha512-97Vba4KBzbYmR5VBs9mWmK+HwIf5mj+/zioxfZhOKeXtx5ZjBk57KFlePf5nxq9QsTtFl0ejnHE3zTC9MHXqyQ== gulp-sourcemaps@1.6.0: version "1.6.0" @@ -8844,55 +8823,6 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^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" - integrity sha1-6UC5690nwyfgqNqxtVkWxTQelXU= - dependencies: - lodash._basecreate "~2.4.1" - lodash._setbinddata "~2.4.1" - lodash._slice "~2.4.1" - lodash.isobject "~2.4.1" - -lodash._basecreate@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-2.4.1.tgz#f8e6f5b578a9e34e541179b56b8eeebf4a287e08" - integrity sha1-+Ob1tXip405UEXm1a47uv0oofgg= - 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" - integrity sha1-fQsmdknLKeehOdAQO3wR+uhOSFE= - 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" - integrity sha1-TTHy595+E0+/KAN2K4FQsyUZZm8= - dependencies: - lodash._basecreate "~2.4.1" - lodash._setbinddata "~2.4.1" - lodash._slice "~2.4.1" - lodash.isobject "~2.4.1" - -lodash._createwrapper@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._createwrapper/-/lodash._createwrapper-2.4.1.tgz#51d6957973da4ed556e37290d8c1a18c53de1607" - integrity sha1-UdaVeXPaTtVW43KQ2MGhjFPeFgc= - 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" @@ -8938,14 +8868,6 @@ lodash._reunescapedhtml@~2.4.1: lodash._htmlescapes "~2.4.1" lodash.keys "~2.4.1" -lodash._setbinddata@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._setbinddata/-/lodash._setbinddata-2.4.1.tgz#f7c200cd1b92ef236b399eecf73c648d17aa94d2" - integrity sha1-98IAzRuS7yNrOZ7s9zxkjReqlNI= - 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" @@ -8953,28 +8875,6 @@ lodash._shimkeys@~2.4.1: 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" - integrity sha1-dFz0GlNZexj2iImFREBe+isG2Q8= - -lodash.assign@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-2.4.1.tgz#84c39596dd71181a97b0652913a7c9675e49b1aa" - integrity sha1-hMOVlt1xGBqXsGUpE6fJZ15Jsao= - 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" - integrity sha1-XRn6AFyMTSNvr0dCx7eh/Kvikmc= - dependencies: - lodash._createwrapper "~2.4.1" - lodash._slice "~2.4.1" - lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -9002,21 +8902,11 @@ 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" - integrity sha1-ZpTP+mX++TH3wxzobHRZfPVg9PE= - lodash.isequal@^4.0.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= -lodash.isfunction@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-2.4.1.tgz#2cfd575c73e498ab57e319b77fa02adef13a94d1" - integrity sha1-LP1XXHPkmKtX4xm3f6Aq3vE6lNE= - lodash.isobject@~2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-2.4.1.tgz#5a2e47fe69953f1ee631a7eba1fe64d2d06558f5" @@ -9043,11 +8933,6 @@ lodash.mergewith@^4.6.1: resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== -lodash.noop@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-2.4.1.tgz#4fb54f816652e5ae10e8f72f717a388c7326538a" - integrity sha1-T7VPgWZS5a4Q6PcvcXo4jHMmU4o= - lodash.padend@^4.6.1: version "4.6.1" resolved "https://registry.yarnpkg.com/lodash.padend/-/lodash.padend-4.6.1.tgz#53ccba047d06e158d311f45da625f4e49e6f166e" @@ -9063,13 +8948,6 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash.support@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.support/-/lodash.support-2.4.1.tgz#320e0b67031673c28d7a2bb5d9e0331a45240515" - integrity sha1-Mg4LZwMWc8KNeiu12eAzGkUkBRU= - 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" @@ -13613,6 +13491,14 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vinyl-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vinyl-buffer/-/vinyl-buffer-1.0.1.tgz#96c1a3479b8c5392542c612029013b5b27f88bbf" + integrity sha1-lsGjR5uMU5JULGEgKQE7Wyf4i78= + dependencies: + bl "^1.2.1" + through2 "^2.0.3" + vinyl-fs@^2.4.4: version "2.4.4" resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-2.4.4.tgz#be6ff3270cb55dfd7d3063640de81f25d7532239" @@ -13659,6 +13545,14 @@ vinyl-fs@^3.0.0: vinyl "^2.0.0" vinyl-sourcemap "^1.1.0" +vinyl-source-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/vinyl-source-stream/-/vinyl-source-stream-2.0.0.tgz#f38a5afb9dd1e93b65d550469ac6182ac4f54b8e" + integrity sha1-84pa+53R6Ttl1VBGmsYYKsT1S44= + dependencies: + through2 "^2.0.3" + vinyl "^2.1.0" + vinyl-sourcemap@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz#92a800593a38703a8cdb11d8b300ad4be63b3e16" From 7fa6686e8c80671c38a6de0d2e8ca5d3f8c9d3e9 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 2 Mar 2020 13:40:45 +0100 Subject: [PATCH 07/36] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8138804665..ac971f4129 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Home Assistant Polymer [![Build Status](https://travis-ci.org/home-assistant/home-assistant-polymer.svg?branch=master)](https://travis-ci.org/home-assistant/home-assistant-polymer) +# Home Assistant Frontend This is the repository for the official [Home Assistant](https://home-assistant.io) frontend. From 5066560411a9e7c411aa2b7a807c9e243ee3e3d6 Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Mon, 2 Mar 2020 12:54:26 +0000 Subject: [PATCH 08/36] [ci skip] Translation update --- translations/ca.json | 78 +++++++++++++++++++++++++++++++++++++- translations/ro.json | 80 +++++++++++++++++++++++++++++++++++++-- translations/ru.json | 2 +- translations/zh-Hans.json | 2 +- 4 files changed, 155 insertions(+), 7 deletions(-) diff --git a/translations/ca.json b/translations/ca.json index 7c2b1af87b..df7fa77c66 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -626,6 +626,7 @@ "enabled_description": "Les entitats desactivades no s’afegiran a Home Assistant.", "enabled_label": "Activa l’entitat", "entity_id": "ID de l'entitat", + "icon_error": "Els icones han de tenir el format 'prefix:nom_icona', per exemple: 'mdi:home'", "name": "Substitució de Nom", "note": "Nota: podria no funcionar amb alguna de les integracions.", "unavailable": "Aquesta entitat no està disponible actualment.", @@ -637,9 +638,37 @@ }, "generic": { "cancel": "Cancel·la", + "close": "tanca", "default_confirmation_title": "Estàs segur?", "ok": "OK" }, + "helper_settings": { + "generic": { + "icon": "Icona", + "initial_value": "Valor inicial", + "name": "Nom" + }, + "input_datetime": { + "has_date": "Data", + "has_time": "Hora" + }, + "input_number": { + "max": "Valor màxim", + "min": "Valor mínim" + }, + "input_select": { + "add": "Afegeix", + "options": "Opcions" + }, + "input_text": { + "max": "Longitud màxima", + "min": "Longitud mínima", + "password": "Contrasenya", + "text": "Text" + }, + "not_editable": "No editable", + "required_error_msg": "Aquest camp és obligatori" + }, "more_info_control": { "dismiss": "Desestimar el diàleg", "edit": "Edita entitat", @@ -1356,6 +1385,24 @@ } }, "header": "Configuració de Home Assistant", + "helpers": { + "dialog": { + "create": "Crea" + }, + "picker": { + "headers": { + "name": "Nom", + "type": "Tipus" + } + }, + "types": { + "input_boolean": "Commuta", + "input_datetime": "Data i/o hora", + "input_number": "Número", + "input_select": "Desplegable", + "input_text": "Text" + } + }, "integrations": { "caption": "Integracions", "config_entry": { @@ -1418,16 +1465,42 @@ }, "introduction": "Aquí pots configurar Home Assistant i els seus components. Encara no és possible configurar-ho tot des de la interfície d'usuari, però hi estem treballant.", "lovelace": { + "caption": "Panells Lovelace", "dashboards": { + "caption": "Panells", + "conf_mode": { + "storage": "Controlat per la UI", + "yaml": "Fitxer YAML" + }, + "confirm_delete": "Estàs segur que vols eliminar aquest panell?", "detail": { "create": "Crea", "delete": "Suprimeix", + "dismiss": "Tanca", + "edit_dashboard": "Edita el panell", + "icon": "Icona de la barra lateral", + "new_dashboard": "Afegeix un nou panell", "require_admin": "Només administrador", + "show_sidebar": "Mostra a la barra lateral", + "title": "Títol de la barra lateral", "update": "Actualitza", "url": "URL" + }, + "picker": { + "add_dashboard": "Afegeix panell", + "headers": { + "conf_mode": "Mètode de configuració", + "filename": "Nom de l'arxiu", + "require_admin": "Només administrador", + "sidebar": "Mostra a la barra lateral", + "title": "Títol" + }, + "open": "Obre panell" } }, + "description": "Configura els teus panells Lovelace", "resources": { + "cant_edit_yaml": "Estàs utilitzant el mode YAML per tant no pots gestionar els recursos des de la interfície d'usuari. Els pots gestionar dins el fitxer 'configuration.yaml'.", "caption": "Recursos", "confirm_delete": "Estàs segur que vols eliminar aquest recurs?", "detail": { @@ -2162,7 +2235,10 @@ "header": "Pren el control de la interfície d'usuari Lovelace", "para": "De manera predeterminada, Home Assistant mantindrà al dia la teva interfície d'usuari, actualitzant-la quan hi hagi noves entitats o nous components de Lovelace disponibles. Si prens el control, ja no es faran aquests canvis automàticament.", "para_sure": "Estàs segur que vols prendre el control de la interfície d'usuari?", - "save": "Prendre el control" + "save": "Prendre el control", + "yaml_config": "Per ajudar a familiaritzar-te, aquí tens la configuració actual del teu panell Lovelace:", + "yaml_control": "Per configurar el panell en mode YAML, crea un fitxer YAML amb el nom que hagis establert a la configuració del panell, o bé el nom per defecte 'ui-lovelace.yaml'.", + "yaml_mode": "Estàs utilitzant el mode YAML per tant no pots editar el panell Lovelace des de la interfície d'usuari. Si vols poder editar-lo des de la UI, elimina la línia 'mode: yaml' del fitxer 'configuration.yaml' en l'apartat de configuració de Lovelace." }, "suggest_card": { "add": "Afegeix a la UI Lovelace", diff --git a/translations/ro.json b/translations/ro.json index e41f9214ac..ff2c60a944 100644 --- a/translations/ro.json +++ b/translations/ro.json @@ -996,7 +996,7 @@ "group": "Refresh grupuri", "heading": "Refresh configurații", "introduction": "Unele părți din Home Assistant se pot reîncărca fără a necesita o repornire. Apăsarea reîncărcării va descărca configurația actuală și va încărca noua configurație.", - "script": "Refresh scenarii" + "script": "Reîncărcați script-uri" }, "server_management": { "heading": "Server de gestionare", @@ -1183,6 +1183,74 @@ "none": "Nimic nu a fost configurat încă" }, "introduction": "Aici este posibil să vă configurați componentele și Home Assistant. Nu este posibilă configurarea de la UI, dar lucrăm la asta.", + "lovelace": { + "dashboards": { + "cant_edit_yaml": "Tablourile de bord definite în YAML nu pot fi editate din UI. Schimbați-le în configuration.yaml.", + "caption": "Tablouri de bord", + "conf_mode": { + "storage": "UI controlat", + "yaml": "Fișier YAML" + }, + "confirm_delete": "Sigur doriți să ștergeți acest tablou de bord?", + "detail": { + "create": "Creează", + "delete": "Șterge", + "dismiss": "Închide", + "edit_dashboard": "Editează tabloul de bord", + "icon": "Pictograma barei laterale", + "new_dashboard": "Adăugați un nou tablou de bord", + "require_admin": "Doar administrator", + "show_sidebar": "Afișați în bara laterală", + "title": "Titlul barei laterale", + "update": "Actualizare", + "url": "Url", + "url_error_msg": "URL-ul nu poate conține spații sau caractere speciale, cu excepția lui _ și -" + }, + "picker": { + "add_dashboard": "Adăugați tablou de bord", + "headers": { + "conf_mode": "Metoda de configurare", + "filename": "Nume de fișier", + "require_admin": "Doar administrator", + "sidebar": "Afișați în bara laterală", + "title": "Titlu" + }, + "open": "Deschide tabloul de bord" + } + }, + "resources": { + "cant_edit_yaml": "Utilizați Lovelace în modul YAML, prin urmare nu vă puteți gestiona resursele prin intermediul UI. Gestionează-le în configuration.yaml.", + "caption": "Resurse", + "confirm_delete": "Sigur doriți să ștergeți această resursă?", + "detail": { + "create": "Creează", + "delete": "Șterge", + "dismiss": "Închide", + "new_resource": "Adăugați o nouă resursă", + "type": "Tip de resursă", + "update": "Actualizare", + "url": "Url", + "url_error_msg": "Url este un câmp obligatoriu", + "warning_header": "Fii precaut!", + "warning_text": "Adăugarea resurselor poate fi periculoasă, asigurați-vă că cunoașteți sursa resursei și aveți încredere în ele. Resursele necorespunzătoare v-ar putea dăuna grav sistemului." + }, + "picker": { + "add_resource": "Adăugați resursă", + "headers": { + "type": "Tip", + "url": "Url" + } + }, + "refresh_body": "Trebuie să reîmprospătați pagina pentru a finaliza eliminarea, doriți să reîmprospătați acum?", + "refresh_header": "Vrei să reîmprospătezi?", + "types": { + "css": "Stylesheet", + "html": "HTML (învechit)", + "js": "Fișier JavaScript (învechit)", + "module": "Modul JavaScript" + } + } + }, "person": { "caption": "Persoane", "description": "Gestionează persoanele pe care Home Assistant le urmărește.", @@ -1219,7 +1287,7 @@ }, "script": { "caption": "Scenariu", - "description": "Creați și editați scenarii", + "description": "Creați și editați scripturi", "editor": { "alias": "Nume", "delete_script": "Ștergere script", @@ -1244,7 +1312,7 @@ "heading": "Reîncărcarea configurației YAML", "introduction": "Unele părți din Home Assistant se pot reîncărca fără a necesita o repornire. Apăsarea reîncărcării va descărca configurația actuală și va încărca noua configurație.", "person": "Reîncărcare persoane", - "scene": "Reîncărca scenarii", + "scene": "Reîncărcați scenarii", "script": "Reîncărcați script-uri", "zone": "Reîncărcare zone" }, @@ -1635,10 +1703,14 @@ }, "save_config": { "cancel": "Nu contează", + "close": "Închide", "header": "Preia controlul asupra interfața dvs. Lovelace", "para": "În mod implicit, Home Assistant va menține interfața dvs. de utilizator, actualizând-o atunci când entitățile noi sau componente Lovelace devin disponibile. Dacă preluați controlul, nu vom mai face automat modificări pentru dvs.", "para_sure": "Sigur doriți să preluați controlul asupra interfeței dvs. de utilizator?", - "save": "Preia controlul" + "save": "Preia controlul", + "yaml_config": "Pentru a vă ajuta să începeți aici este configurarea curentă a acestui tablou de bord:", + "yaml_control": "Pentru a prelua controlul în modul YAML, creați un fișier YAML cu numele pe care l-ați specificat în configurația dvs. pentru acest tablou de bord sau implicit „ui-lovelace.yaml”.", + "yaml_mode": "Utilizați modul YAML, ceea ce înseamnă că nu puteți modifica configurația Lovelace din UI. Dacă doriți să schimbați Lovelace din interfața de utilizator, eliminați „modul: yaml” din configurația dvs. Lovelace din „configuration.yaml”." }, "suggest_card": { "add": "Adăugați la Lovelace UI", diff --git a/translations/ru.json b/translations/ru.json index 107f4aff6a..dbfab3657d 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -2055,7 +2055,7 @@ } }, "changed_toast": { - "message": "Конфигурация пользовательского интерфейса была изменена, обновить эту страницу?", + "message": "Конфигурация этой панели Lovelace была изменена, обновить страницу?", "refresh": "Обновить" }, "editor": { diff --git a/translations/zh-Hans.json b/translations/zh-Hans.json index 000302303e..2aa1eca1a2 100644 --- a/translations/zh-Hans.json +++ b/translations/zh-Hans.json @@ -1499,7 +1499,7 @@ "title": "侧边栏标题", "update": "更新", "url": "网址", - "url_error_msg": "网址中不可包含除 - 和 _ 以外的特殊字符及空格" + "url_error_msg": "网址不可包含除 - 和 _ 以外的特殊字符及空格" }, "picker": { "add_dashboard": "添加仪表盘", From d392695ab75ab48461cbfe42ba56ec1f0ccd5d8b Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 2 Mar 2020 16:55:16 +0100 Subject: [PATCH 09/36] Recreate translations when changed on dev (#5042) --- build-scripts/gulp/gather-static.js | 6 ++++++ build-scripts/gulp/translations.js | 7 ++++--- build-scripts/gulp/webpack.js | 7 ++++++- build-scripts/paths.js | 2 ++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/build-scripts/gulp/gather-static.js b/build-scripts/gulp/gather-static.js index 17a1a87ae8..9c1d843567 100644 --- a/build-scripts/gulp/gather-static.js +++ b/build-scripts/gulp/gather-static.js @@ -65,6 +65,12 @@ function copyMapPanel(staticDir) { ); } +gulp.task("copy-translations", (done) => { + const staticDir = paths.static; + copyTranslations(staticDir); + done(); +}); + gulp.task("copy-static", (done) => { const staticDir = paths.static; const staticPath = genStaticPath(paths.static); diff --git a/build-scripts/gulp/translations.js b/build-scripts/gulp/translations.js index 80e2e72213..4b0d203448 100755 --- a/build-scripts/gulp/translations.js +++ b/build-scripts/gulp/translations.js @@ -12,6 +12,7 @@ const rename = require("gulp-rename"); const transform = require("gulp-json-transform"); const { mapFiles } = require("../util"); const env = require("../env"); +const paths = require("../paths"); const inDir = "translations"; const workDir = "build-translations"; @@ -144,7 +145,7 @@ gulp.task( "create-test-translation", gulp.series("create-test-metadata", function createTestTranslation() { return gulp - .src("src/translations/en.json") + .src(path.join(paths.translations_src, "en.json")) .pipe( transform(function(data, file) { return recursiveEmpty(data); @@ -166,7 +167,7 @@ gulp.task( */ gulp.task("build-master-translation", function() { return gulp - .src("src/translations/en.json") + .src(path.join(paths.translations_src, "en.json")) .pipe( transform(function(data, file) { return lokaliseTransform(data, data, file); @@ -341,7 +342,7 @@ gulp.task( return gulp .src( [ - "src/translations/translationMetadata.json", + path.join(paths.translations_src, "translationMetadata.json"), workDir + "/testMetadata.json", workDir + "/translationFingerprints.json", ], diff --git a/build-scripts/gulp/webpack.js b/build-scripts/gulp/webpack.js index b6730f699d..f6769a8c83 100644 --- a/build-scripts/gulp/webpack.js +++ b/build-scripts/gulp/webpack.js @@ -3,6 +3,7 @@ const gulp = require("gulp"); const webpack = require("webpack"); const WebpackDevServer = require("webpack-dev-server"); const log = require("fancy-log"); +const path = require("path"); const paths = require("../paths"); const { createAppConfig, @@ -58,9 +59,13 @@ const handler = (done) => (err, stats) => { gulp.task("webpack-watch-app", () => { // we are not calling done, so this command will run forever webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch( - {}, + { ignored: /build-translations/ }, handler() ); + gulp.watch( + path.join(paths.translations_src, "en.json"), + gulp.series("build-translations", "copy-translations") + ); }); gulp.task( diff --git a/build-scripts/paths.js b/build-scripts/paths.js index f95395dca6..315cd554db 100644 --- a/build-scripts/paths.js +++ b/build-scripts/paths.js @@ -29,4 +29,6 @@ module.exports = { hassio_dir: path.resolve(__dirname, "../hassio"), hassio_root: path.resolve(__dirname, "../hassio/build"), hassio_publicPath: "/api/hassio/app/", + + translations_src: path.resolve(__dirname, "../src/translations"), }; From 1d1688093ac255e75c5e9ac00202562c356806ee Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 3 Mar 2020 11:10:57 +0100 Subject: [PATCH 10/36] Update config.yml --- .github/ISSUE_TEMPLATE/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 7581c03f17..b7fc0dc4fb 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,7 +1,7 @@ blank_issues_enabled: false contact_links: - name: Report a bug that is NOT related to the UI, Frontend or Lovelace - url: https://github.com/home-assistant/home-assistant/issues + url: https://github.com/home-assistant/core/issues about: This is the issue tracker for our frontend. Please report other issues with the backend repository. - name: Report incorrect or missing information on our website url: https://github.com/home-assistant/home-assistant.io/issues From 7e48b2176747eacd744e53509f0a66ad56051bbd Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 3 Mar 2020 16:27:35 +0100 Subject: [PATCH 11/36] Remove states-ui and allow setting (local) default lovelace panel (#5043) * Remove states-ui and allow setting (local) default lovelace panel * Remove from demo * Delete ha-cards.js * Add default for yaml defined dashboards * Update ha-config-lovelace-dashboards.ts --- .github/ISSUE_TEMPLATE/BUG_REPORT.md | 23 +- polymer.json | 6 +- src/cards/ha-badges-card.ts | 45 -- src/cards/ha-camera-card.js | 127 ----- src/cards/ha-card-chooser.js | 81 ---- src/cards/ha-entities-card.js | 182 ------- src/cards/ha-media_player-card.js | 409 ---------------- src/cards/ha-persistent_notification-card.js | 76 --- src/cards/ha-plant-card.js | 165 ------- src/cards/ha-weather-card.js | 383 --------------- src/components/ha-cards.js | 374 -------------- src/components/ha-sidebar.ts | 15 +- src/fake_data/demo_panels.ts | 14 - src/layouts/home-assistant-main.ts | 2 +- src/layouts/partial-panel-resolver.ts | 10 +- .../dialog-lovelace-dashboard-detail.ts | 170 ++++--- .../ha-config-lovelace-dashboards.ts | 20 +- .../info/developer-tools-info.ts | 37 -- src/panels/kiosk/ha-panel-kiosk.js | 27 -- src/panels/states/ha-panel-states.js | 456 ------------------ src/translations/en.json | 4 +- 21 files changed, 134 insertions(+), 2492 deletions(-) delete mode 100644 src/cards/ha-badges-card.ts delete mode 100644 src/cards/ha-camera-card.js delete mode 100644 src/cards/ha-card-chooser.js delete mode 100644 src/cards/ha-entities-card.js delete mode 100644 src/cards/ha-media_player-card.js delete mode 100644 src/cards/ha-persistent_notification-card.js delete mode 100644 src/cards/ha-plant-card.js delete mode 100644 src/cards/ha-weather-card.js delete mode 100644 src/components/ha-cards.js delete mode 100644 src/panels/kiosk/ha-panel-kiosk.js delete mode 100644 src/panels/states/ha-panel-states.js diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.md b/.github/ISSUE_TEMPLATE/BUG_REPORT.md index f088f4c019..72b32a8ba7 100644 --- a/.github/ISSUE_TEMPLATE/BUG_REPORT.md +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.md @@ -3,6 +3,7 @@ name: Report a bug with the UI, Frontend or Lovelace about: Report an issue related to the Home Assistant frontend. labels: bug --- + + ## Checklist - [ ] I have updated to the latest available Home Assistant version. @@ -17,21 +19,22 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w - [ ] I have tried a different browser to see if it is related to my browser. ## The problem + - ## Expected behavior - - ## Steps to reproduce + - ## Environment + -- Home Assistant release with the issue: -- Last working Home Assistant release (if known): -- UI Type (States or Lovelace): -- Browser and browser version: -- Operating system: +- Home Assistant release with the issue: +- Last working Home Assistant release (if known): +- Browser and browser version: +- Operating system: ## Problem-relevant configuration + {n>^Hi}ed zd!70GCc~qNZ=_t2N98W%os98J8p3t$ooOoXa};~Sd;5DvPeL2{nDDM`D)EtrJuY%~ zPjv|%)s9Q@r?5J)ARk}+MR3)W18HQ`b`u4`S82)FPf}x6o`DAc0FWyTjeh3ACNe3& zadMK6cLk>J+ccLdcOiHOuKZcz;(NZbaoP-liX9_Pa7K9aT7F;5nH61{FB1N&q@xez z>8u=&E0(m|agtc%l~e9KU;h9P1Z{@AA&WurTpl-i2~!)p<6-ji2c7mQz^}838R?VE zzX_0GN6uv)(qci^}seH&s@rMNqhuijVG8P$7`aOdnj zDY<1FPh0QtN<8t=1pSS=c#JvX)#kq%QrW&Q3TWf+kSrXH9s@HP4xF^j!qf`=Dvq5e zp~PVpl(CzIjM6N%^on{-IoOS(8Dvw}z4;AXdah7^W4A|$mNj6MB{6QkYgqX?H8oFy z&%Ix7iWzAxW8zohK+cKP=4Th=!Fc)0yP_p!?QDid#9&(9S|yVumy(I&%|=xj89UJ@ zkDD0b@HcRoF7!VcDbn{~j#haaF!IQ}x;1R8bMBkFCoV3C;WSA^?T^6%%}WXnBE!x0p6c6RZ$=i-mHf}boAW()IU zF*l_+BC&2ge2UUZ+mLWSck|?OmjV!L-NT={< zfzY8FfZ;ZpTV|i}1&HfEF|PPbne+1R1}{C!qwBgz+zSOdgnBa1we<&m<@tt@`?FMn zH%x0fO0Ma>Z)U3=DZ*I&`?-f>zXzBQ!s*v^lLg)(+x-ee6nThIiaOFbPoF5b+IJ~vkm<(+lT zY4GY@SBDMJWAye8IS;oSXJsYnuy_YVbg%||@_OpsMIls_@5uGg^u-cRLrW2; z*$R}R>V3I1d8l3H{{R)5a+ehpk6n2ykcP3QI!23p+NJzE8>7`*jZxoz2mb)`SXHGw zF{Zr8zy6cT2U!YejoAFJ4pqfijE;JQ<%vAaO zi$1~ehKGL?qXmw(8}GNT;Lsy3&+;<&aPF3qtv1>C>v`mQEfs>n)xNhS*=Ij!*eLBE z@m_N>o<8#jgfjjX^wu&hXN=;mx*;+1!~B%wI>& zC|yz0-LAi>NygIn#+7XNq9%>-IC|5aqc#M1HWGWUo;4}D*{)-K=J0VmS8zH6z+a&ZJ(Hszh z9G`c}@i}6WI6VzKUm~^PGZ>@g*0iT>80sz|G{~J*85Wg2Q{>uR!3SV;qn5-Fnrl%K zvum#Wrbv6MyX`0IMD#duc54XPmq6rJ)NT?*R-(VSF)Nd|S8n?^J^Z^#yDfAk&rhtm zrj_N`J2K1TN$|_=G9-y~#;027p4zJO_7(U3W!Ht*hF1<;MoTg9>n~eAu&2(IQpTEW zk0+d!S5T14Sh~fT=rVF~a&b?%saw{$>UN2CkhFr?M!ZVBcW|9SyX3*Ufofso)~@rq zaHrtAtk;vUQSe^8X2YKa<;!nf;}$z{4P3)2@$%JW^1Y~i?~kiXrxlA>%Sr79g533t zhs)hwp4yI_jCS2XT$i@SGWboAi)x_To}QT1Bag<`>39`#n!XhyA|V8H$55pJ6Vb|Z zDxc?@le?(vs+d{@)bUmC?76yC0wZ8j>H#hb6o?BAa^-%p5B zOhVoaCajfKY5c9X5enr_nU!5m>#`?gdMoi1xkMzud4`xlsr%tR)T%4-%WK))BdXH; zC+4|!3eE~m;*_r|DEEh#J9%=y4V8AUwPsGESDz*iP|^5(v|0fDv0M%a`)jwEg{Fue(Fp`DT(rIp zF@4bKy}LH+q+*U3eEh(ynSz=oD${0|eH4@8&a3ft;n`f|n061o)|s+bzowOLvfmwF z2RClfOL7@GO_U?Yg6b1FGst!v3de<{)w{hv2E|0rHQhH@$Zfe?r?RyA&lRf&sR>~E z#o(DNxV7dlHCGuMIiVM<8ATVH2Vs^1BMIWp(8x+e$W=PC&`43~YN7b1?PW*jjqz?{ zIX^5yF%@-q25O1zlU!a+7nP(Ykn-Cnv)CWg6&?!icpT0Hx?1ctmBgxya#Mrvc#VQT z?+TP{dEAjONs!l=wNxVzk49mTPs3d|$t}y^(s+`@*Q9pK9cc)a{Ug=}MW;=F=f?}GyF;W(1&viyQu8(z0ig5Qd z(yWf`;*x4Pibo%Y1Z;}z)LcIKSy>NEWWCf_Z1&eZ+qp(O8@k{Pnf9s`+n8}oH9qq4 z-ZiabQnjAbmEItklB~Qj5I41|198cU*=9X%l_^s#cV6P}?m9_!WPw_!CYx{XUdY!Y z9^8V=QW9@toKImnNz^MEp3MO^ekGAbAHN{E3Yy8aZnJ7_wiZS@ zpcM^WIJ?h=R`Zq$O}WpY^U8zuBJnzwMDy@V)?Toal)xgd#lEWC3#!dvZEt`AZ}SYsDnUJr6myE>zMF zkie@~#E{8$a9Wt&V%}kM3bruiih8y}G5WWETRUTBQTbMrBO|&*`DsFW#91+VqdT^0 zu&q_yHW^J^gO^-z$&KjyrTUHtzS!@~JhJ6B3tDs1<#l34P-9E3Gt+jj6w(yJuR^xN zGB&nHIEN35k3+Ii>#F#CUQD|-q%y5`7T|{;89TAEDkKk?XuHb_BrmO*O3u6$+z8}l zcX@leD?3@LGH~$nJE4@;6P>(c7ax9v_Am2ZlY+E1?) zf>6iRHJgYw>X14VxjjXXGntN;gF_f5uaxKVTXD&r1Dhb#D$0*E$mKRr`?oUefoONw zwkT4O>_+xILP#Q@8k0;F=BA{c7`zxJqsOC{RL+)Z-ipca_@QK#yH`i!I)}^nQ3b|P z?nTv@*W4$u29c0tqVB*&kf~QF!EuKJ%9U}zN^S7)6O?M;qUx>iI}*oi2j`vs_tt(9 z$aUhJwG4;B@gvTFMmb*`N!nVB3ImGk)B=oUu5x65+ z8{uOfpb~y>gRNBdUTP|Yyqt;R2-9->G;7@nX8KClL=Qj`JN#@j>Ozy z;dun9EnRVxFua^NzD#^!qSPmB&Z+i*lwB|YlfYKtYd$x`#54tsEFX}OTXg^Rfy z1cUXASci1rgt2GL$mZ-h+kY}?x>2H(bap$i9fv-?=$TJ?c`Fz;sOM8gb#0oTZTKyFMDql;zFsPd6^WoVRZk zOnjKNade$geDYVl*NacQm zrH@w>2-6678X9CeA1-H|b|+{PwQI#TnBKGaIQ#IEx&6@ew!r))i(|QxFO-y%hAAMV zYT8St-0JqDS=GRWGR3ItL_m+10Y2)A7M>)e@MgtDuTuos8>tU|puPPQZQYu9;~2WD zA);hvxavo)vb-ZHG0onbo5HX>tOM8UiA_F8tEzadMd`JDm+>svG`lMm9)8_b>&QGC z6@;$-{de*?eq9yU?+li4N3QMjZT8f$(8#@e5DE#pZvnn8Dq8RlN|WM{ieVxFG+E#t z=c%`+jM4G(_#q-{qnb0>O!`{)oM3<)tVu>pj(#_V(bl%f_fijz48kEY*Pg_Jk;{&= z@}*mFOfp+H)I|00U2o~Omc>+hdWzbPTViabR%d_&in6URm4hv6zmpoj3eVXRrVMKU zEE~4&x`N3R*0w2LcL@7@L~cywkF}6R@&e^8Ld0V|S!~@xG_%3rsmwh)IlRj>XF7nn zNbAI}#w*A%#=mYJ+(X^74|k#LOtttx_xyZ8vssYzLmQ@V!Rq|gz_v%a%tKye{czID zu18N#dg}ujrP){I&+wfCsaE-087`b#ajz*ME&vTr87I4Z=#f-BzcY+Ehc3mP#v*b@ za$b_bno+1pcl)v=6k|g-@a3l-SFWp!EX&Q`i{U}y)8L>_0~%A{Bo4mnk1kADmcfHj zUxyMrFC0HFU47S&>G}2dJ~y-0nuJynS+C1PZV2CPJ}SN+mS=LmbK`(Qa$P@zVmRB5 z+WVSZN{d)8+_C;vvgteu6Al)((C{oV5}P*Z;CQL)?rjeTm{7g<+583+JH4*Q7Y-PS zw3BUo{>ykgF5HP=Ylf7@FdoFk@Yv#2ojDSGIT}_*r4E%iq9{LRPlnGT#=PAd?l=>> zTJ5Ird?uF;t+oFEmvMi-y8i&nxS!u$f92d?@2TN0*y%S! zF1nY3V@1P;epc|QTD;9FH24suT8+zAz{kiW(LG~@j{v+RyZ-?BE&s#-Mi2l20|EpD z1OW*L0|EvD0{{U40ud4+10pd&GErf15EDXyQUoJVa*?62!O;{jGeBbD@E}9+Q-WY) zfRduJG(?lq;{VzJ2mt{A4L<_^0N2?&qZ96{r3XbPRpwF_#BPJ4`lmXcs;G_7nD#*I zqJ5Ec5UPmXIO=vzbOJO@a28C6S%bZk%voXTo+mEK32JvOmapmb!%V8`RaFp)o*Sy_ z7DYJ7wum}vRZK#zq~j`>gx5^ME|{ebimIj)h%*WCcwCbVfeR5+2dWyT=pi>-YxE?* zHK$I7bWW6iufd{I8B*l1{{H|5!o!bwT*KvZPn%WA5omO%o;3DUg^POf@B(h=M8Up@ zHcv$p#+$ixA|&R~2Gy*}dn&u3RS+DBA9`ttbDxxOx8BGxo)Fzc zsAhq5Y$7Ok0qsFfRTy_-9^U!O?jw)Jo_TTlPh!m=OuN!0J_gqtPFfX70SkqT*rA z-4MT{Sy6RO!4jBu-B4F5RGXLeLkJ6)PsMLoR2>@&7Y;q)7E*r{>7RS2;??-CHLJ{W zk;FQGAP6b4n>GavjCdQC**&z`Gw)b{csdi~eoK@E6s$SFb)cP;M5Z#I0nDh4lT7zf zNkB%a+KVQ(E}Y6!8%htEFq(EL#^M5LE~$Gi{w99)O(-^-Lgt!e5TeyUL`JFRRdwuz z#)z4VCfRpf)IlN$PBKb})elSsJkb!QL?R;awkT*(7t|iFhOl&NvyaiCEomC}a{u-wSx;v_NGPXRGf0F&FKM=6~Nc^CF3Al|8`%puy zuQ2KsSgW!Q%N>)L9e#VDL|IAIy;WYW$m8XN+*XHomFAsM99>o&+&`%~;1nJ%0`m@` zP*X4Ms&hln-m%(Gzx*fS7M}YrGxALm>!`Ga4U_JwAByug3uX0Hf0CuKB2_LRMU^mH z8m7M1(#nr?K1@+&u&GpQysEujm+cu*2wX_OjhBZ<@1Sim{w2p{;qTQ3++35?+DiOp@cyt9&CR-4^$0|z1U}L%`wXF$` z8E8f35r2d%wp9vsoA|25h#c3NSM5KNZeUa)h6wg4xL^-^s)yjK!&SHO zQ9y>uhfmqzZA9$9XcZM(e--Ard6Q)n8RLa`5cFs+et;)kL6RSw#4kLi(%fuHFYl^;I%gTb1=w-{BC}uFLAL zs*Z#q%rC0Gs_cf`(^^V!oz@d)p-vOJGgNd$NKbieCj`t&e)Jntob>w#GoSJU6&An& zc?|?NWzEaVD>a&q<$2CnK-7EqXgoRv+rom2t>8QZ6O8`=pt|G2nMBHfJZ^kDqo;$6 z_){mqrnX9`CRrVqbTtZ9NNeH&R24v9vY^p$jag4*%d_gQJsj*_f99OXH0lO!yxVB3CzfF}rv~csYG#&QufOOl#m>;?}By8z%0G zV-avovM%szKur>hieo$+Wnr_zpd_wC!XPRNpn8zOpdM<0>Qd{3%RlO`KlNB9eYZy+ z^H2*W$Ubal-^gJ;)ipiTvIH);Y0jY85%wnIP}-V@^eTvW9MK2LKK=oyrY`HLPB%RK zIj4AHPlsjK!L5TI=r{40Lg={odQQop1TKq;0G|qzS>Qz>ePz_5E_wJr5T6c+Ct;FP zT2BhZWkyASO}THE2(Y=eH%03JY_MIF7OX&Ev?pGg6Q2@WWzm|a58v16UT3SfhwWuz zXJ_WWYyA8?%PbNB_t(OZf&TzOli=Q}Z$1T*yRPU63Z&B$E9-c)M~O0nW576oh1E%| z@Z^r<_&@$U37uEgUs*i(2JWUjDPCEyMW*wj{gqNzl2h%XE+2x!4lZ;k)}%3R7THiA zzwWgdw7$?H9 z_$WYVyzN4ysy}{p@cO8Da)tF*KMtQp^YG;hqgOuzuZPt`OMfK$4)U90-$XTK3Alxg z19iM9G{h%PRPR-y44p{r{u9+42l1I+U*x}LY5(0F%~h#p+x_W+3kk98sjD&B4Om6g3`{>t{{V50u?NGN zFQOZ#^F0hGr`bh5$Y=|R2$?<;DXyq@M_6$Znjjz{d^H$AMya}7r*Tc?4JE{<87an3 z^J7)doqtq?uSWO zf~cNi7F_36tWzQ16n?7_zE)J+LFOHcc9jy>J32%O{BspxO!Eg(o?wq0I5+KjQxqOZZ;4)JqP~) z3&Eo>jXS12ktSmQ0D5Ch#_&zT#Mll4{nf=KlUsZAO|w||5!GQ?_!zjFM&Sn#8qt38 zoCSV+zXT0AH}*vQ?GBdqva@pV`>bD_YX*RLa{6hSE@eHWvEx;Rh%*zC`z#}u3HlJ3 zW`cB-=C#$kiK>3Y!hUdnxpW&)#fG!y5Wl3A=_2&(=UrCPubLbo!0opiBf} zhu)xv3mVD$(BSh-M|Ckv8JVM^BnLPeMD+`%kehb9V$-s7gNu*ki%oBTIYfOi7V%^T zgQ3lI@tXW3Y@dN4kFjhn9xV!f81{=obuRMFD=eB=^YbZ*E<2;%7X%|*kp1|D9vo-QEp&&g+m@2H?g`TNfvn2 zLc@RqdRpralTf+$=&=BB`N(N2hZ){!lq5HHl;IpS0EvTFtFm#s9h#Kgo^Kr!foq@e zwEqBgz%d2;EJoCdj!-wp@lg!Io;D+CAw7M&$|92Lye1lOB_aR8dj0?$HY#EBFHiq z?D}re(MJ+<{IW}7u4!0C%sO{W#i&xS-&k<_)ekWej$sFg6!&Fmd>)rfYpMC$wiEqc zXW_|K3%+wzts_+9GcuhvbV~mK&K5gp2_3?0I!5>u$`UZ)-$5R6q0nJQ%r#FBiTGr z6YQ{YDH=nEBm}XQkH!##P<(aF7YlH3wbxlsk_o&0*7A;EUUT~=e+n&0-3yAea81oY z^%UkyZ%e(9(mt7JlRw5(gqY%jj)aypbO#!)Dj;zA4XzsAZUo_3nGPf!>*O%g+8z=g| z$XHpS+|ScuL}Byhd@{5NzP4CT${TZJbPI*`T(`2N*kreeD04FFJyYKqxY^;X5J6!9SJva0hu zy}GOzp|sU*=<*&R)dLxtpv$o@B^(-^Sf)D#SMbh;$Wq~#{IdB?F&bL`0Ahk%)5!k- zf2O%}RnbGQ?wV$TEgSvmm&B`nP>FM>(`b-Weoc8Abxgu*2xDq$b~M&souDZ!A1Hs? z4|P=^QvU#PLd7r?;W>6w^N`b!DavWF3ub|`eW@F!I`d=>V%w}y9LHoCYY8JaJ9K z0Pdfus9beXO(z`}NAYx>6CX9P$3*thUT#27u=Ir3ca(SLuwM8c8zH8%s)jy-R0E>MH^Ia-D`8&+6n9hqcca(J{gGpHTe~Hf9y+{{Xo^ zH<QSMvO>YIIOlbsb`x^F+T5mzf^z_#((ZoU z*5&$lgx!9c2nZ3?5-SI5=o9tcfJ~+c1>Pm>*;W`LDnmn6%ZwbP-WHZ|&)KZ%ZQ z_9lwlFvLf{=ASN!sqmaCW1iu3KA3ge{;{$ys*XiXR_vR?K#5qo&FLJMEm{xYso}xA zbXa6m2p@7HmOq00if5v^JqTEHLx7|>^{FsRjqwGC*$OK%Lw>~fW|1^>p2!Lq>^n3Q z%>9XsxMj)b5{?}%X%l{un~fxgOM)(G^RZ5YvW&QqXMHXBF1B6Qe;#ARh|DGzB*9f$ z)%hX>3`3_*NJd7EF~7VOwk|KC6CDo2XpnY8Q^Ih{!))fwS_1xisE=fLS@L!)CW?+b z^_1dqAAyG0rDMLYFqwN(aVZBzzpV33Y@dinHHZviI?OT1A{|@(I&JjfPB!V_9034b zJ665cJd@o3mqiCfft9RhtZ#LXYVq}A4}kN);mI=edZjpKaCmDR-b)eFGns)u{lIE0 z_Elx0BOX5_^Xhd@9>`=3!}xubG?qVPUMZ!pHS2XGT-Ns6bWJ(LvB|UZe+TRI>|HRP z%3wL>1G4a=jORNBF4$O&tg(gxPnWUWN3^QA-NJTUR7KkDwd}q(>51-EBls99qz*`$ zTH}Tym4-j)taB#^N>dqXSw3RTtTL0?0C*cVg;*u7H

}!uB(tG~8U+WV=b(G`q{v z{r;ThQy$Ufw^s>RjF$fZK`yeXAI!hBn=KpEi2+`woE;29ExUE;ovgo-7_=1!!`B`hY7%2vF@-NFD*PLr{1Qy1RHbw z6~WT|N50ADA%^Ny$qrzG28UD|<{BE(0qmUMKZns#5Gse?@lL8vY~2%cKKrPe!MV57 zGnx_L*g|tedZ7&2TN?gK^t7GPdwj;>w^7X&nkjZ|Y?{|S!TLBvXq}_!lOvL+=Y#F` zEE0Tz!65sUjA66$jvUh)Q^qstZ%Zs^RV;qng}4~jE&T4kh%?*|x0>Q1ujA|8J@nz@ zVz)@>kFD5nJEB@lNTC{`^|&Jv8u^f0+f?j0Wmd3H*lI2G!$xNTspJz|m9fQi9o!Mg zPG}pc1kUX!wv%g{pkxo~jSlM!kjM~cnx>aD8Q=6qNDYBYwv*XTQ3b`;SeymV;pdbh zGk><$j%b>302NC+riuUn=IVxesW<+WKR5GdH%L!*T|=5VfwCe7T7f5S>xauYABQv; zf7{`{>F+hLFjyVbwtxk~2KAqP6NJ^b((?feHluh=*}_7TzYkPRE^9aBQFU#1L~R3@ z(LBXCd@oFN`IyG@$f|^4;ru+2hhh=&6u}Jh99=)=ix6IjU~g4x5#|_FOn@A}taTZdxmnDa12*0_PrY26F8%uwm6 z-F}y4yg4hm?rXL~hZABG*(?q8i{(wxuR54PuKk?h<+=ms=YO?fw6_bJk0pz%-eNh~ zVY`bByGJ!@t8oLhxu%G4!^bp0d3%WdPvYi=$oznjZqrF3zTbol+-?GKZ zaM;a9a;)~$>foRa6*|y1TRDyURv`=mZSIRpi=tibnP-ES-3D+mv9B_VVWsA)Ai@Z4 z5xNWze5YTcIbP6RWjRR4i=Ydc^OeU!qh#6Qbh*Ru{nm}1Xx+|@moekcWG(KUpgXTW zgrhRF4eGg0-B9)Zi{fz1bA(?%r1eo#3C z<9leE#__*I&26F_Se0qeA?YWHYlTVI`cE>Nb6`zh;Y57hPPy)?f-v?)G)I9s*hk(F zGRGUuv?%U2DCpMV{)yWrx8WBEy2N4MvU8^r&;i*qQ&$*M0z4vCC4}w+55Kfr9 zAauiQ$WOeu)Mu10Djz`&4M1fsX1?fd?5jv_J;Hlwd3(51Tw`YS3N!gxK;%;#8v3^C zha(}MyE?AE9FX)!2L$Mgq zYEjJ6UnVvHc-MD0XgMngB1<}i1P24Juv(3XMa>1cV~gE6*-dT~{{UrCYbP6p7g9{M z)|BHMH$~+C01V`J_)|sR<5#lA(*?6}{gbevtrw_uKf4!GpcjR$E(_y}E^W9+XJ7AB z5_vfol}#tI0!1ha3DG*hdY-2v8OxDT&oTpGnJlUM$X-o5`b;L}a9(7C@Rg&^^ru;} z(fnZwM^GuEl*5AQ%E8-cEcnvVRp`bb^ zn74t@Oi4}ScCwia!wnok49+IiTSVjYd9*sh#yQy+WGRCe9bBp%y6mW|GgJuA;Q{2KB8>Y+6aJblsP}diKos+Re+U?~(T8eqL zCbV{yIf*To1sNE|48PqCyuDMnC)5;OTgt(k5!MvO^x>>3ckSCIwOayM9@I5xT^VO_a(Av|5q(T*FYJ_C3`W~uj~qtvStu%1^=KBhz!0N-gS zCf&g{_8TBF`H!)Uf}_lt*$MIpwfF3vCS>GyZmKw3=xBO5X^Hn(BEzStH$dsBt>%kl zP*bkQ2J8O-D7KoawOnQM7-{okh%w)S6PPS80W!jFdQR?f&W#XgWa zKv8z{PQr_gA$gHp>(OfGlXT-F5yo8Q{TxTTmL1QQZ#`3*=HU(FS^JZnhJVks92qm+ zHx{PLI?8Ao6zwy_{mLb-b$TayTFlKWT$q2^dR*s*&`=*ahUd)nQyyS#RD15C_{E>Z zUYEviv-3^hh?kzfqI|Z8I3Vh<P&fJ@8m$`&SgW*snB$CxZSbTGxWtRU*8$UsvzX_64(AJmChf*A*dtX?-& zzoh#rrx1(OHIzhP;tiZGv5g*SZk<$lA^f|lvvP1h*)Ye{p%yESnXlL_M~Yyqa_J%TsQ)T1;#t;%|#g)N=a!7U-jPF?}hS%;a^ z%{XRerQK)uM^rY&-8YZR(cT)vmd@m@tU@Q^ml3gYr;AUeYbu8~H|b=CMDD z>@j9`O^^AyaGVXU6U`7A1yRH={#h}`9Q|gmr zWN2WjDwFZ2&0qXqKHjtQYGwHOH7(Im>Z3SlTk2v(Y(}QGH== zhEFbNy0XooHpa1-KtnKYf;uDNhV1QTuz(I}bEOBF4Z5$1*^5ZoUAiXX2yqItw+`YH zUAr~BN@K);{xorKh;?xN*(eSSM#>t{a;}S$+bjA)JE)Kl!fDf-syIp*6pyufob#u$ zGfVLXRwipXFo)`#DZ|5N>*V>}{{WcdqKchu9Vykz2<8fNWEdQiaD_%)&iX611eIBe zqnfI3Ch58c2&o&9tK5Cm8pBAbI!Bmi?o>uLBb!x|W-%VlpNbfi;x>5to{F0A@N0q< zIiB20MMIyi2nc#IR5~bOwt8c36S@C4DJHN0p4yU!or)2YqB2(%16#k zNSVpcWbd1B;yp^%m9p=;d#ZIJb9Yr|&9c$Pc$OE=F{jj?+UPtc$-~^N6PL@j_e{@K zIj_w+9fgR|8gx#xv5te;4D4{y5Ov<(NAyE0OSTJ|#c5>nQ3i5_MaB=PcUXW{#u}~7 zVmSGDn#yU)Xf|+Fi8Q!HqwHGQP|{rfjz<<+1>)mmFQudRT*}tN!P^bd-M^xLrH@P& z^2|!Z`91VhHMM`*n!*i7YcKkaKax5UGN3HLa)j#;*DH)T*87zFP}58PAJsb;FhSe= zl}U`e?fS2iVf^3bnTK1=f9C10oUE$HRM%7^C{7l{#ex`|F9K=oHc<}DE#2Ru4uE)B zenWKMl$mXc^2J7v!C_+LpHdjvaE`ZB<{|dOO=e0oCz?0%-ei*rjviPL1BZ31E5)iW zqAjzVg})_(x2F9QbfZDhMvuC6p@x!wvcx$tF>=CoS5iGZ)AF*bTxw_+I@O&AWZPsDD2uVfMHIm23GYq9-Tir> zB;nwRALn(nSpBeum52%AlTLv1Ov)QGWE%WH>Q);`#lxGXyHR@&`B>PrwWM8RVl-vF zf(InH#g-=lcj>x3Y{E4^{{Zrt#@dWK-_*1v_;A*aja*H8m}UZP+9?4X6T~%dGxn+m zCK#PR$W8#hEk|WR-bszhW+X5?PWr-(jKXCz(K)8(TMrKro8TebI1ZCJ83Z*}sa zsmur1gx5~|lgR=2g2~(^rr>s1AMKx6P8Q`I7Otsi^QYdN*2_1iYp!7InTf4Z*ISa~ zr&hnRXU{c`+p^+(erd!>*HwmT6th-@L;)OfRt8eZf+b|a;1!yHu8JpI!IVivT0w@k~x-UU`cK;!Jc z-uZxODY|^sDVG3#>?}cM4N$nl0>kx3MgS}R$Y|U%Q;&198Qp}&xSr|wnZeph^K=K( zJMvH|pxgVWAA;T1)oixI5(oEQr?{>X_imxB!Vv&T{>V0iMaWFUFKnKD`X@47ZPpaK zDs|wFe9_sO{i-KC06$W&_+EH^#l@UY_6j~N)-Da%ALZ_`G;jT@6c(|>@;6>w;+pv5 zRq5XFs+P3r39?Sqbh_pQ0;;L*CJ9Xx5HN{pZs?4p!)2(NSRVcYwes1iSR$h5zi!E< z&1SXX%w1NhpEoWryMgYhglaW32=i%*}4 zud-_h^IyI8M^fk6yMw!qa$$Lw7q3l zT)`GC+BEKs1eZpFOXC)xae@;hxVvj`(s<+U?t~D61q)7N!8N!hxVwbpan2d{j`x1R z+hc#(->Pa?t(vuJ%{kNl0lW+mdWF3)QZIPSW{1}vju1&T)@dI+S5}((>D&>kNi3pK9rFfa}pz5%I9tNE9cQOlr^6! z>yiqx<`K*|CVe}S?^oHlu#Oi()igvwPxiBms~&wqq~+F#+ndL$pR22}Yc~Z#KRdGZ0lC z)ffvhIMGm28DnwKN6U~tyYw4Io#mrF$N*1LTT9jF?hh)TsWWB9IHJa?&Ixj#elx3e z4XcJHc3Py&%q&Id9IE(E?3h18>Hhwb2}-{IpcC_HJ}|F06Va^o)ya~hxw?TuW~6Js zc>*(Hut@MlOXP%kAU3wGKkL~SZkbH zmb^n~5;f`K+s)AKy>YR{;%W26s_|b1{NcmBExkpvJ_4LeNCd8`NDypmz z*h(Yppn{vNx==eBqviGPI_GyI0nDY(6cwUFg$<%_>%>XOYS53~1yti#@cD;=M?`fR zfE~geH6G$5pU|b8*8Q1&751Vf4dDf8j+Z6XZRmQU9kixBCY5 z+XMaqA|h5RT7?8wv4-3o_wW!(Ac`|~nc>oC{+UJb_6puqr1m8XYD|)+7xII27fVXw zm!It0A3o=%@&|lVyAg94eq@Ib`N|c6g}u<*C1*HVn0{4PFQMx=&&#dv3l?x{@lSU1 zi`*mB4s)DGRF;R~^g8^DheQa<(xcqs3#NTWsDaU!NlEfthA&`dLB3b9pTwB%NoMQ} zM>7V!{sCk-W<^oge9qBi+uob%1ixOR8QsZEKh8%i^q}%IElIFBdY%7f%TXm!a}#g@ zdB4*h4l8kMtsbEMEi_O14=~sLQm@Gl0sw(X$jHbj$Ve|PV=oU90f3H2>ZMdOBpFm( z59ysg*VwuMLt+2NhfI?0#s0^{&`MNeCqelC@0YyD2#$?^;9kR+%q;nk5Q82rre(sP zz^*_gY``DB;qDt*n)CreqN%b*XZFTUuAusxRK^$o-YMBP;Ex~|%cV&RdVq_|Ywp>~ zR|`&34J%X_5ZN;$M8guY1rd%>BLO85dQ+1LuJOCG0fQ?39@*2RXe|=jhpSA@>&Ab8 zy}?OQ7BU4~v=_~8zhJXP`Ksyu$h@Z}<+d|3aHn_~8YZp~Zsi*K6)~P|WYpXI0{%n& zZ-qgAAGLp7aqp`wk^y_rc)RF?T#`b!8N`#3q}wCMz3HUt&vp5{ZU>-mq!_+;)PHu0p$MG>sR`bYqm(JVuvVBd%TCUwT7y@juDe4*~hS@^dm5m zXGNNO&tTDwR(Z$p(bUOz0W?zse00Nq8WZo-bWJR@L zF0Il+hMxi^^hl$g7Eg5Q^T^?OR&IG7elTr|b1^kE>lDJbWyGqMc}?&Wv%KgNtoAs) z8&%b!?^#RPTn#dO06YbCO;1}6lrEF@txz12hnk!dfw3OzC1?r?e@dca1%$#921J&o z#7^uL4Nd$Mxk~UN#^RpXPkNeAoz&E`sl_ozexJ1X)(-Z88KZ~D%;0)x_Q3WZAT%6n zEf2yp{W+dG%AfT8zKS08d12!ML_H~LwG!P`^|u09ubK*2+F+@wb?9WYKWg_ErK1Q(<{{;IT?h;mCC2|L({r{#@2y}mqE#!GVa@v5PJbX zwR}5Kly~8YM3{Vcs{1Dth&{91PSb`c&gAs3zKg8}jZMe4DO8G@A}HOrX*6Eg?;+0O z0fDXS;J_YM_a?iz5BjCEjMfzpz=OMkriR4yC8r1HLz>ziN@Q4?VMQY?`;;tN;@&cFSA||p zc>%2e$Oy>VT2#b|@+FPSKy4bFm1B5YB67fT8Jfv;1*Da$!8ztaYL%xk;0Z)B7mdcE~+93ya7Z^$m;)JQ*NGL#T5Q@`x__S)25sOOL@l-uc z7H-Ouj5r#=O9P1@3E@gyhN|_pwZ#8j-iv@>PhUQp5_+f7&t?N=Hp|UW45G2XPVEgs zC=qQFiV<0OxJtrKjkq$nS_}((+=|YDb=?Z0Cn9?dKke~0aC6sy;W_HLX#;FZjtJ~N z<#RA{q6*}|0n|UjSs8O#dpZcsGJ8x~AA>mlc81(}if?!^WBBnZ!ds1BRCocDgWhI( z1|`ks{yhU9GA5&+PKxjx*0v-rZawPSX^Gy4VhyLWPuqw62>94X>nzFb^M2U^^7bKI zBLta{AJ^J%gaJBn!lfRGu8r7EQ^~Cz& zMTUOkFH!t7JUBXY+3OW>c7UvdzEiYM?6siE`xzotRN`f8bk2I0m*#QHo$`x~_obKh zrf|Hj7FgMWFO$%43gw9Xm_ONV*i6Cg78(8qn>PHb#+qCq0gx_B+#QRI1ii*S$((8`GPlDDEVXfl2f&Uh;nmwE<4FOd zD<+>uc4uc?Wxn53-edwG=}AnTs*ZM&d)j~#P|$E9FW0;VuSHE{AwXq}{lva=CLk#7 z0I(Krc4mYnkoDC#S_T7M;+%R{Z+Da=zkD{#WSYV(R)IeXZF~WSLN;hv4CJKbcZFja z3hCh2#p1b$b6OrSY^DIa3VB@V1IY|}hGSdIDgn9a6kmGAGna2L1v5JGOt{ z>$Am)4yC?U-uDaJhn*s*N`Nw0tl+!}rC48PhLnm=Ux9MMqc!IBg!mEs7ie@mYLDf* zq+r!RnHhYfEaw>jrnQK_+({%Fl{e;;p<(Ep!~SN_=2*j|)P8D?rCvH58tvg`1|(j^ zmVzTmZ{xj++Jt|$*%HQv#-IpVyVp0}gWvKQOOeM_~ZoAG-uwz~zs>{uhQv zGY`f@3PE_$1Cl)^bPP!$JV-?{xpt%ik@zL*O*ta!;j+Ko?)Y}`e?d(4L$FUIja#Pt z#bCbBeY54q&M|+x_E3Kmr5Gy!4P95%ouqs+~$>{75aO$A_Uw|hM8nx8S(5-6 zQLmUVOp@RM`g3qYLwGakAI`GS66$*(uw^j#9QAbqh&i+^gMmNjk;r0%bp>Kw1Qz@U z82L{O{`2-bFygoAPtIqVc(PKblumm9=RZIpt$KOk5i1p?O*apd+E_9yg5jihyUqYb zQ;KUWX>8OdkoK6<>)ls9LwdH~6E1~yFBLHDumN;V?B}ZV^dPXJqTL0sRC6>2fGb7$ z7o0N!)A0_OC+yjGpHNObHK?OO1uix=F@gD(+`(A-UnF?~tI5J;%;+lQTrBIIBYf3w zH2wkNV4r`ch@VN(=mvf~j3QL|1=lD-)|7Et?rQ$e zvaSvfv<2}M$Fh!t>)&1UYd6nQW2ikBLMypie!OQO@kUZpRAy@sSAWMFFE&nfCAp}o z`7v;;LUT%%VsY@PaCh%vk#vRW`+i91>!ziH1sccig}RL&ZyP>F#@*+z<#7E`@)--I zsWkvG-@pCK8yu3~^^4n`CIw+Cy?1XHwBbNLn0QW*1dN0MN;{;@@n@lb(tOrN z0Vfh=n^8EDvkKTlb;58=q;vW`sBr(EqB=Gn$s#Jzg1!-h@6yMEjED$atv{+x;qBhk z>C71S$aa&r3vK>82iJ{{tKXwf@I+LWm`cd_2wE5MWhbZ4@CuQG%Fst`!ijIA$!^ep z_xO{=lYL~KITCf_VoX%_d+W$cQV{2Y&tsBDaYj$7$@8V})8DDWA|}&)z(#3l}{K>o9P4G;21H`~%z-MxS;2{S}#zy`znjHuwn}Qy`1o z`T3toH3tO`rp~-FTg6B$QF2&tXxA7iEM4b(YHb3gR{+T4Q{AD9%S@d_oqxpHF0cJs z?eRJHMPYm+A!s6oe9uI*s1Ke8TkjK9>1sa{(7)L#pF5b8s*w*CR-e5)!8hRuI`yDx zRN0;}Vd(tTyE*}bWeH;kOsuF4z-bD)(SwGc1w_nyasC0Q#@)^37o1+FsMVc~fyeY- zrS+MM_6f4hJqJ(WfqP`^^jkd?Q-L`UP<3R&loA4W?~J*#hvcr4#ak>Y{7+J@QPA-P z(f@ih7nMc4iAB~>%yUuHbl%=b z1agX|_Jxn6*^gWo@~F?3_(e;D{Yc#Z0OfHvBwj)+e+oJ;Euxb)V1JRhMrUI=HQvU7huUStrpaDe+s22alt~Vb#T;*gg^Sq(Mh3&f^JH%=7M0i`0j1jE&1YURk z)U7ELbGAAsl2+=oL>9r^#khT0u=;p-C*CVxR`qkDKiW)8{hrCaw>K+V)4u>F`jAj$ zCRiwC>Ao2yaF6+X@>Yh*>VorY1s-jh#K@Q}^-$oXJL-BF;hb79HRt;}f#%miSL7{@XQ8xi(NoCvr~1p9T296_{2x=Np48|HOk^xslbR z&p;NFOUcmIYCFc#Zww9-H>Zz!SGYZSVMCdPX!AJ&^@)#6UYIfAqb_;d@-qM={${)s z1yQHhi5$<=W9X87kzDOp9D0GYXd@C8B?j8v8lq7N_;nKYWeW4h=F zfsUJ%PkLC;sA^i=uT|HC+v~A*ezR{S=hbf;Uol#PYEY%Ep}KC!RKRff>%yqY*6IND z=`%^6ri0pJYxH2KA!C&$mIpmD0D~TRqkJt(A**C)6T%rSb%1%j{@;$4!vU#aJ`5C) z47x8$f(7ChjcllrIo|;(19?es8>DO!3tuFCDOAdQ5i=*>n9iEGD17KPyzbWZi937A z*q@QF^X*T(NVga}fRtenB#y8eE_DCYv_Ak##C%bPIG~3LW;^?*1^~c>Ve!#Hc|u-} zMzjA}>VJ8rvFwxNd{LxIvfSFSbg8TQxziSlQIKoEVY%%|yxNmyKQBL26bu8Mm-G$XzmoTOt=!k`)`ts5AuWt0} z5fJJXX)=5?{cmL9j_YCmKxNQ|X_IwFHeMPB8g{|h_;jKQtgl|WIeIY~F;?g`YFzP0s>lvpRkRBBD_k`@_a}B?jA-tFlPaoFN~0FcP&ODGZw6Th010t|hMcfH#|?N=n@=reUsuU&oOV$H&W z8Ri1|*essS&)*f!MF>tnn8jS9{{+1WlF0bD^T}PjBl2~PV;2yvgBE}EDD!3d*AR== z{$jvqSY#P#Q+B80MD~2yk@i%MxBU{p)FVY0IHC0}2E%_hvUR0C! z!qa~XlKV)2KuAPQk4bcbNcEKCNUJ>8JTEzemp$GFjmDB-T&%Gk{)cF)#&8wthCgLO zTpfCf8Ci-)dQQQY6})tH7`Z0%!G6e}8McnptNc;=L;Q^t+fSr!G5GQQF0nQl!>*I& zrDTDPZ@sUhLZ5XwiZ0%c;Kb9Z><;M(-U;|gR&%=6MrZ(vuX$%<=;}5zSUYWCedMbdiP$3zcRThp&IdQ;gx+B2jwe&J{X|Dg22My5ki1m1n|cjrJ+mLG9T1cqEB*Sa0jKU|xcmwmss$BEC% z+3B+i3}CXlRts@CT)AzUzPayAISWd{r#h=+7$<|31uujXPMUC+rI|L ztH&}-}sgm>>EJ-S*;sV-TOzXyfNz2YGMsSf&~ zP)41Ps`+Dnou;|iWljgGg`Xz0_>dtDBb0$r*Z>*Y06$NoQ0-D>n5#BNAGdMxH81cG zQq&7_j+|L~kDwko{+L>`|RxYcWAM z6>}DWcYV5F`OPV~2w<;!Z)3!v;49V{elki`RP}}F(sARwq-G8LpV*w9iXMTX2)7(s zgkLkJ7L2Pu2V1b$_nFIG|XxEN#vXB?0i+d@Rviss9@~0J)+Rc%&nME zN|mP+@nIQ7Z(ZSh?Y5#5T&48}nI=W(gyR|diw@7C)jB2+dZ^e~ zbwVwRHl*9vMMv#~LpSQ1sbakMhp^EEk1p}yzrU8b;^W)F_c`Ghw^%3NZX>@>-3%}~ z@L;wRn@bN5MlzDq=voB)T?}ri&)a`g#rE^soUtH*T8f*b7eXxaU8-sN_ek4pqZIxe)q`h}vGY0|nZVS* zYEQP~4mTOnpzLfnssc4xf{`u|`0!H4_Ma%Ut=8wLXH`uQ=vX69=OqZyabLiQyr!IL zwO5>A5>#v+RFO<%cg$bumx~Orr>ed5XTMQ8SrGfuH21=bVqBh&qP!q3rbXet2Hnfz z++<4pgg^#KB7dF5E@9vFo6ZGbbq-0dy0Q{LB@DR-w?eD7>tzySA{Vvv2K*7@45 z(8!u#VfFLc{jo&=gCvVgCq-<*$G!h-Huqodgl}O#CA{O1?g|sXLf4RKy8RA!;p(wbwAGIntIsSRA%YU`U%e;Is_vCG!orGDTl8-6mF$KB_cTIVb0g2- zW=%XGO?sR4%kKXTSZP?0p-O8R1A$41&|&0oePBA=LJ)_eRaP$IR5T{%2}1_)v4&A#s32g12HohC;$BCnD-Ar zj9kzpJZCnr&M)sj$zAX*nDE5{i{Ih(p3(VZYpz6dD9xUU8CiPML6=v?P1x_@fR5h$ z;{0n|cZBfg*)S19v|X;;m{SapyLK0kEvw?39XtCr}pV=Eyf< zn?plD7TJj@2*-Qubm4ktOI1^Y(I|E^^70G9*6}{ui5uhN!hep{7lMm9WW)1=3`QU# zw}L`5c%KXX(rj_hla(6S+rkw`wf;$JV1?g@8CZ$`vl->7M?MS}VCSbjxS@xvmL_Ac zY9bZRsx-MG&frTE7HX&$)%|Oa95NYmkw_|;^(9RUZfMi-hrFU1pXMj!bWPR==S6qH zrPQoT!4NR{eQh<>rP7?LwG`x_@_=-ID~{~tHmBmh&3f}eA$yYz#D1&uY}Db7*mtia zWFC#18=A)=5WN^{F}1AnEX|ojHz*mK%5KkD4H;Ui0K zqT*SgeA?J32B1Rej-y!V>I`_wS)@eOa|SGJiitl@?}U$4?J)W z$u{T=#z(%5)QcUvg4fY08`SKlNmsmzd=Xyx-~KmHj_Q^KN9x2F)um3 zjv7-2b{@#IG>MboSWojLW%9jxYoRF`x>*S3d5HIx_4fZZDLDgS%PaqUnZ=kI(L`c@pk8>9I^ke`+Mg4+g_0ZS-g0U(2z-^&vR) z*#WzH-r_PE2hsD6fp#QcC?PT~n5U#xEi=BR3NuqQa_YxF4Z0y6Gz^VD+zk->eY>?$ z4YmptxbBk@7O#lD!OY6StT<|1fpcwO} zru$iaFRUB2RdhM-!20<;f#$~R%U$niI{n4?-9>XCWhu*VADHt!PO6*ejzt@iFI-h zUtyr* z6Z?5|RVSk}MU$Srw}tA%aJs=rIElJ!94gnZFsBYt^oduOwC{6&)EVm*Z);X&V|`_J zecA2$NztO-twKBh7C)fG)_SF0N!sk7W-1^s31f5hsDtKz z#&HWkhje?yfe(?Z4XgaV7h`bfyVguE)2K1moXOORu1~i={>0hJQwa`62NODb#otz% zGGM3*zINHL-L*HyR*4l4=eqauE;5s$7CIa>vAH6!H3U_UVLrC-Gc zy2keqmBz4b()71jMM4y)w?+fKH@Vm@y18(2vxv^Qc3lh~^ z-YlJXPc>ePy@6kXR3>tja$@w-C7yA5q!nD)ch_=$$kR086It&w%kYoc^KH^hQrBkI zUM;-u!8mAYVEQrTGdQ5~ChVtstHvoI@Wd%z)`cn2Nn$<+Tf-(}71R)ug^kdxKu6oE zUCNF8OF8E9*I9YZR9W)!eXDMGsZeK`TTVLSxOtz>f%b`AmfhnfH%di=QD;5!_e7YV z;J}zxaZ!=B-1YQO%xTgpt2+jjZ~Qy!v^3XckrX;CNw_XUwym@v;(325%$J})G7TH} z^LlVcnEwa0!KYkStV~?;%w3f07Xo&3)AD2%$%dl%Y+T4YAcZAO|c@C~tYpjMWE@3&{XGuiXy>YF~pV^?R%jI+fo(Y5Ca?;w| z%c$`aFHG0sUEL{)A6;zRc4&N>b(+m!(O>chrM9x9a=5I4OleWbd!LbhIWC~}t4r+0 z-QC?&4U=2&`7i+mIH=8*7T7dXUv9pXuI_cZY)HJt$!7#LSoqn+|8wwuBu?pue0&Y7 z$--Y>tk+cc3slK3m!?UQ#myT!kYlieqMm9gj%$KDrDDYY%?rJjYqAQVpmj;u^xt!xFuymgmwWH>*+2EEs zhzo==BpaLSkOdBF2|7{@qWds@)&K-d!Ljy(O7%Dxl?TyUH3pnTH>|z>b6!vI-&NiL z-;Gu0(B|e1l10jGi30KZsxt$izVJpZkX`S^@}x1x$iXJ ztyJheU6}b=J)m2^zMXCrv{^vAtzYRa2>oi4R7bo%TenO1A$-}-YX7p;4 zB)yfRx(w!{ybZwxr_B(u-Wzz4{O4Izdm4`76*A2~s+CJR$2=6XND0NV+myYq?)LI! z^?`{E$VJG)Ckbn-Q17@9%9iWKXTjItxg*|L0l#ca4`zm;zeSimo`38U)$asqSU{-p zI6O$y8EV{CV4P`fZ%QaNoU@F@d*;13f%GuyiYB#O2EEJgKw0=D?i@C|V%@nij|lj1 zN-3l4(k^8#u+5gy=^7r@^z{%qKz>O$oPD{c0V=g`@rS9BIRQr@q5zf5f(2Nb7}28B zB;*}6V<^vM@>|}n%6>DQ{ZG$GWDE8t8w~3q{J7bP*13{v6SHb#7mcW>af#AUTaVJO zwJTM^cB>=!`J4gxH{;_YnQQ>m4pawN&9Y0wa^RALlqZ-B1KaAVkvhJ3ju;}6q;w_r zF0efbBcpig7po;#i@=ajEr((fHDu=_^a_+%?kt%?V&^yo%WD8LQjZF0C8f+5MZ?H9 z4K$b*=Ppq%8MAqkIDiBx{fNAY?wxJ@mVbL4hPVn1y*OU;-=wKkBKR~J%0Lwu8{)aN zDsUcZEOQ2G;26X&jxN~w$@@4iQ+L4Qsa+5mA%+&x>LqP~KNSy6SF-)^zLrRjgQnj+ zPpZeozlDE>A3ZPsgtpeW@;#(G7KaXjE*hc2;{HRoq0AY4gl3PsP^*tww0?Ogt^oQ6 zn2eW890R#)t{fosg%_EP2ZJIMuq8smKMBg9q_8K$5H5Jjo#bt&X%Dm` zwaXJUX&0hL9>=QJQY)!5NYlJB4b(+wg=MwB(6B+Mm=SKWMR`XvhH87J(g3H1q_C+0 z79poiso-W2k04BTJw01%OaY9I3UYc4I0*?ebe-5FRyR+203bGwybeGEURP!7%@3B% z*kA`=2ys-YH`N{h{G)a-IQlX8Ln6t5=fZk}EAvfImBC0+1~V#tH2$WP0Z;gZ;zcSJ z*|h_Ox%}y#PzuC0&JTt;fCcl9c=Z?|O`dMBFnIVhq}xBN<^qxm61ZaBYXgcc*yO!Y zJ&tIIC%+%Y#-NX~cEcx!N3&s-bt9)JqzX_b_y#%wi_w$o#Uc_PG z->VwzRSXiBWM63=G`mJJTHq2}9`=s`Zh#bzN~6J%pOok$=brGh;idSa-P>3IIQb#0 zH0cNw)1PUU~Pye%q{s*Y@L9oJ99rTX!KyLXkmQP;fR2NI=%pfH1QcK@=h-6lD(l>%D_ISs=*Kp4@U^)VQxE+oZJ3r=6U` zEU7Uw3Mlc`RPEcS`k|#QV+VM`Gc4*{af{;itRC$hd3j&JmrLiHA6d1?PEwH{GS^6M z|AvM{eV@7cEj#(M3lt&>pR6R*Cw6$7!|jmB#V`gHJUYSg^(UWV%I)3xbAQJ#LO8Qw zJh%E>{@JuO3O-VmOKYc~T0XKhgBB97Tk^^g@oFZRs~m2BD_wVBVKd6PCo^aNYAaHz$DWmbBGq^$_@M;jrU_6Z3W-!VETP2;vRT}5R9NX zS?75+Js|6N8~7^WHj<4WJQEr}l-Oh>N)2^=s}y_?wyghNR;)=N+s6i{0h_07xhLea zqOP1GJ5pu7`9jSohUN65==m9FomU*QHvOqRpf=C#|lzHoY=*;n+)nE-P`%BPcr`duXJL&1kMeq<}JZs0?tdv`ScdcE>;G|Bnv zj>RV^uCTebd}Y)4<0ty-@Tyt+)}vqsqi!sswjD*4oSVt~hZ#J(dK!Qr?JDJ$M+y+s zIejXzEmx)Rg?Xmf<{w!=dFf7bdLfjnBD_Z3TqQFrb++<%wyc{V!tFP>&2R%Yq~E2s zo90s`CL5&t{kGigQpb&+t6Jp7EL#7mJO9lY>&Dbd9g6_OaWKr?uc%4hNIMB*S>RLm z+!#pdqC;-M(rPZeLG3&_K84#K1ufOrrj$y0ho1kH;N`gPYS=`9bqzW)`^W5$u7L|! z_i-+E>|Fr3$sep`$z$q=kRUt5I(BiCstMAPcXz@2&W<=oJ}ic&y7eT+Ux4l=}NjF*1Vz zSUm^sA(kCw|Ey=b~L1uEqh*ltRybhZFb(dWD9N30r5!74Y1 zApLDT`8UO0e4a^YzqIcs!tFnZ>1RjXCGh)2wpLPjQy$^_8zwD3@9Z@blgbqz;OR1=TQK*B z)KH~>71G8TB&GqwrYb#r7B>CrSk4TnHI3bb?fDQTfM@E{KupR9 z_Oc9~G<>7@R$R*yy!7%H(<`ASsn!v3jX1<8YkgO18t-HKd0baQqAGd^k!sfqO=(Q#afP?^bw<{PY>Xd6lSmYRC$B_8IEy*_o96= zq;9|MX!X00U&!OI`Uko+|3jX7ktEoO538W~uNNP0Ib@6eLtg1|2gPt8yBzENV4GwQ?bx*-*Un)duXB|cLhQb2+uH(BDOiY z(XX7=734CqRz@$1{-2qv7!DvY)`=pWL6t~&^1MX*VT9PK2zPnEOVlB1T4Mt?5@-pj zaTaKWMywvd0E{6-){|1M+8~G=9Z6o4SqAG(FATbD8>XeUj@lRfd-F+tY-E?J_u2%>l%v;E_r;DsfopzhM;rmYHJpW+wY$&pv z1AX8!9l+DD^>w(6Qd)qNly=)B0-z04`Gnod;E^~J@KBrw4wSYfDDgLSw zv_%dEGPA`h7-A(4TwG;=D1EbheeoiZs%hJ$0%Le@87YvrDYqImehpfXQSOl#prE61 z;kk57kr~`xvf-(zPcc2I6x=)_KNM0*7XHeVLyS^Ec2jHm(Btwfgg>2H0Ka}I(?ba< zUX=P>9k)fB;vU4=K)$d&_d)04dr>Cg2kcUx8D~|CT*zVcEss7D(${|g!ode&@Sto4 z87?_@K}yrb;)Wj?YtWGB-~?Z7NP-|eN|2c6^1zz*Nj^iV4U0Eu3O8D;4zVF_wIk@k-7Mri81aARz{wKO za_IH3U%2m0Q*7|EH}YfTJbr((tr?=P*-# zn(Ml+QZM+N zC5lRmdp_x9n4s7Ni%H?^X&20+mF-I8vZk7^?t7R&=pd67u`H47l8~qmmj!VDDhR4+ zoZ&!S8t!mEfmCjr3tuwvPiZpRie)zOe&bKa@YIV?UZd0mxA=xH!tT&&agN#cSF*Fs z+(Rnu5;^z=7ae&pP%qUgKU{`r@MU4Is-47F4BesrEpC_*w>D5=xkmZ=xC0H^^Z;Ud z_EBWStg)MoE_51~(zs~WG8O5Zv)EV@@g`c~;S0x{WW1D315(k|+Sg!E`;u{}mwSx)0iu!nRY^`j zw+Kozj<15h&3p{02)Gi96~BN~$@_{5Lxr~7c#iry(&v=e6&(^sHt+jkMi151u{C2h zm>xGTkm%qRg?wZ%4cD{yHq0yn!08n+*(R?Hd{V~NthSSW*hagZz+c5)6KJvd>bicu z-kOgtd5DvwV>9p9x_i`qRMe}=$O>&V$fS*5jw7QWv9KQI_PvGu#tQ&lA@rzAXMQoj zfz|YDRd*3)*D3OUFuxRXxI(qmYwXR);x+9aqVf`|Ff#3ETG0Js!#OAowAkgoE{Z*| z$ni&HvI|mLI0qX({$NVPJ3J)rEgqGQ#QyMNmwHr@=>oH)&|+ghXS z!}{)+QUI5ikMy+G#el|4bmVi-cN`37tQN4NtKWxi=XaKdw#>6ImSnF1m0(}-MG5mo z$ESPoB3%|RK(AD;vg6gv?>5W@J3X-k#0C6!hwfYN{$bQ={B0>J6lXl%x8`mtJsP!e zG|aoK;^ATo(iukJuPXU6&gIuSs49*a6>l}f+k0p65V%j}UE@Uu_c1wrWT^Hhf}uT{ zdnf$*vI8G^)HP*_$41TeKf7llAZG+6d^XVuNS&bqqJD{az}Y1P_e$=T1;&ea8{nxf zqWx`Iobh!G-%u*#2Epl(QQ@q7fWpajvxfDXY?cG`w>Qkz1NK(;CUMaNSp{A-U%N%w7t)^8T+zbWZ&i>Tu?Mi)qj9ID> zKBN|0^;dWqW{8ajK^I%Xsle<2g4H2;l&`7p4HyX9q4zm|)tUkpcX2Mq_cwgjvJ1*M z9BpOI16EqPrVdU84TF9^CHH`hKL|iXoVNTJ^xAWc_pzlPs}EkSs(tQHucDU7nADhR z+lJ81nMr~?AnhRnP8p@Mde%QgCj=dr!?jPP`|T?DQ*54+5pP;b2ouK!S-+YA=nX^x zY;xpW$w)48V$QgQ{8fiYi_*pvEw^F&+WZ@^qObbKd`;O*44*Y>(5p+ieK$pwL`I{J z3{*)c8ql&yVJO_;cu9togaH}m=sbZ<6R-VFLF0&cMK9TDQK!HNvp!C9z^jd5j)aenv+RbKVR(w}5 zP*InAHHNN1Zg3!47qx7NS?<~p{nJX9j%QI8Yc3X@A5qESiXa}-6?Bx7@}ymLyGbWG zOnW9M={+1NsRV7E36=?-4fuyW%76`jZT6lB7^6X*1lT2TU{Z-3GT2MQi?ZN>A`+hY z6nPBzWtd#@&TZNjeL*(u8|9NDsYVQ3+I^~_J5p^2BrS$u%TGw@RFlLOSldhA5+Y3# zfWUEj(%9@C_a1b#vJJblSX>pEOMex$J1?zv$Lk}UklMQJ*yf#rre)NaW=FPr)M+9bI)wt?Cf!gnx#958L zk}fui4c&m|mC>z*0@MOXE%iAS3v;pL+ST7_q05}$KKB<%Uc>HHLBd;lft#MrH1F3bCWT={j?E)) zxG(}9D9S#T*ksx_H{y6+eC4apY%$h2>jrgL#g%qL#AY83HB6};0Y{HSlWX+ngtYzCe~Df+bM3C95ylpG$_?W z0d`1F8X=siz-3Q-jAf25gck@6)^QU{qkeRG=Q^SLbgg#!9?GWZ$1HD_65+h$z>OL4 zIuhUc%k&JwR6kQ^OM1FXv36DCNTxfrL>lV?bMH;)FD!ao2%)>t!cZ9(Y3Q;-8jr$Z z*ZVfr&;0iG!U^M7B*RG+Q;nrt{P)=%0^8$Wn|%T!Du+rla|R*|pGJ!@K8%w86wfmy z=-d+cIH;)zC+ID-Xb-3d#!|J_dmfinl{Kl4!DzX%K;9(&PoN$l)}!xV+a- zk)~0p>Mb=p-JrQtjs{U(orF#Ei^3)uxj%U*AmobIAI00kffz66)4R&GanIgWWLLJA zg4LgkT*dU+_wb|Jur`t**V)jbnfp>kDT5A1jm?Hv25K9WD#)VaaEf2M;^OyKh)e)5r}|J-zCHMW=i z7d-rjLw$UOM76S{%4!jf#p}Uks$zS-Ae4#szhl~8p?mQo;fBiN&Z)_TB3W@FaJbgh zJ_|GbQLXk#Hm{1cTRuUlw~Us(?QmclFdF==Qt^OhQzF>=ZZi&P+YAcQ8<*XypiqSI z$edqIehS@angjWT>6exuF!fR=)`u0oTlqIO(@P!`Oh$LjJO5~gxbzbu@wXhHYEG>_ z1P(FR*|dc(Pdxl~J_uq+j!v?^0GPS}V7@yji61u+!iG~JF5@^)zRN$J$_vcRtL2<# zP(gF^&5LMaQko6aq`S6z@A>xy-oPSoEqAxD)_0r6YW+jcnuHaC;8xu0mP_JS!PhY( z{2#rT@DJE3xK(|!{n*iNmHZrVLC z9_Z2xYk5=C7?hr6j_gJo3Ztk4x28G0W!1-O^yaW#_vn2uFspfTB6`;-AE zQ}X-vVRTH<**1g;9U^k3_5d_}(L2ZW&l}TXS-n|o74VK?|28Kd8&Hde_LS|sKcKyH z9791j^R6*H1a)Dixr4V;1VJerSn34#zyszq+mKvMcOXMeL1!8fMLbYWCYOHWetnXuf^Y)Y%^nNh=uPhc30rhnDg|2LrX|jpib_q zIIA65RyeXQmLoT32g_uDImeY87V(IWjJqT8)2C2$rS6ToOF7>FbYRh1?GJP z-feQw{2Ds9L!ZZi8X?=&t!w$qRQyp$otf+Nl-Qy@pvdt|-UTVi8NDOIiD%Hj59m?j zzp!#uP8@JD_X=J@@u59PAw!Is5F+HY9B2V`rxK?cES{qLl91z;GQ~g4%H{)&hjpO(abFGb)jj=4ch3x zi+|MbhPDY$hyBS4VH?p8{5eAPiHM>^n~5&lpa?^Rl(tm3LjQFC1E5s@f1l6)-_Ppm?weD3a~M)-y~Ol=Waq`` zr_cWYF&^lTi`7isW^CI{y#LmV?gBp_y0ZFHYU5xwzYYyt&BkU=wrx+g?e^;X`Cb28b93H0=XLmao-*(hs<+p}tf!Pymksa2%hED^>n^_O zY3oA;hQ-t1oWVkIGZVMCdT}Nq6xM1Zg{bl*$varqFsnxC%6vZ^6%{ggI@#=b$VvBe z_Z!se(MoYo69K89_!+dc zZj-BH(V@oqT2Al43A;;h?%j=E!g1=s6a1hfZqz*_aS>T4TGO_fdJXD&n5IB1P7t zcI0Gw4?9~f$@7^s@{l07^qc)w2L6mv2C_bGHikxAG=gOwhZ^T4b{C^m{9UsWS+(09 zC%+xdMa1oudynK5v^ON?Vcd63RvDBAw!P{yc17?wDvs{=4qyU05izsCx1quth4~#$ zBfjnoXuV+m?w1=Y1X@(a6M1iyOtkOk@IQK3bzOw}>s}Iu$#lcMmTa?@w~zcKyvM`j zyPt@>!&g9S$cC0s;vg|;+JDfZ1m%U_^764~C3}0O+&*ivr3hlw+ z>#IeM0MS1{jp>Zprh^{!P?o@|c5h)<_A@CAUiEbH@ArD$l?02H6ld67Gv4I(j=K(W z)<4uZNtZC;!TZ--N&oOSLC^OA(Q9@ zR+!qq`<`;1C2lsO7f;_d<;%ZxY!SktbqYQ5P6|GQ&U7B#3YrIzNa{GN)mYHltOm>D zx=+`})$sBL&i*o5#MP&^SzSqbZs9pZ#ufA4Pm^R&3C436^C^EJA`bwX)*Uoa%7pY9 zdZuJO@mx3^drZca`~z@**%bJuo^QK#>BtE{b`?=-duV+M?smwyY$L`T`=Y&W(JPex zd{)K!c40Z#)HtaNcgK@v)p7as7KbM(^)z$C+7A9e6sEEH2;D3yTV;{@)oVnhVwt%9 zx&GjE?tOdmcx}OGlAW1HAdfG$S=gr^IV;xIIz&}!9~QY|LL#|TA3bL(J@1B;ft64> zk<*?x<_S>rq~j|lIu;w?2%-a&bVUpEs33^@UO^+C&W73v2kt9vD;y&K#aTMUxrhhf z5fa!@jXXjSnX1+F>nMY-?zOMXCpw(_@BF@4cj>eXy=lQ&c=W1vz2T6q5tQ&lUkC~j zAR^>y%pU8oVM{6Irn#L`(g`VWms`0KX7Nvb!%t_JIS12pWO(at@oZCfMzfJQDWr4j z)A7|{c7W9KxBqmK$6>jAem#~nZ#Us;vcJ9eu?cltSm$W^&37nKTh}!2T^!}aDaa0# zIXr6nJ2P;XcYp1$W=9k=ys6>n0`2#+o<-|LlO2O>=^#y}tg^IQbMw&_zvPvi6WdRH z>Thz|45q6K$WYdS){Y=7m4!PF^M8{+I}qi>zk!C#C`v<(+(DaNRBx{YW)<H zfRg}5R8(jqtvE=Lp}546#}zz;Uynu4y?506P2GzuvrU`3X+FXdcV|qq*hhq$hK7bo znB0YZH1|ZTc|vOa1GsXgKi&piM!5TVZ|f2*SD>x_#C8<5z*q{?(s_|aUeKZx86T;| zyNWs=F}yEZ|1b$8u`91!#Qid%TGkSG3N@9b$-Cg!Z{?en2-Wz2g7ka{o3h0^&M`+7 z{IzpCx^jhoS>!t$$XWX&u7I`65-BjoITmaE<0JGdy9nliykyAL(5XU)@zV4MGRn7C zlr-D9xviq-&1us=K^C3Y9=v?xR+E`Pq!AStfM^XJn?$Il29 zWWT;c;iW0zQd`j%8$`pCuy=mi$Z_fVmHxk3=7ZH5F79x~CJbZVRV9&IxFuV~QIl9s z;rutK)S&g|r0=<=%FO)v*=)EMO8f;Q$lv?>@TZSw!giUdtF8TwIQj^`3SzMP+~h=| z*4nOqqeb{|WNoUhUS=aL5?xAlY$a@)0$b2W1bZ)Ob5GC_>{YQ>=z$8v6^vPqWmDjD3Hr z$^?An+X%yFn-S%&hA&TC1GSl{C0eQ>s!%KaFipkOWy$>Ji^w3t5dDymV3l}Gp~+0X z{kk)-{ns9Cct;c1FQB?m`&3R*Ic`~fC&+X;)89xY8D+@a>6TQ$bL>U7DAC>!jAFO*CnDt=FqagKL}-#nz}0c zMM&UP%KHMYiFxKvay3k;A1sd`$WSfmR4O^Ojlh#hN&J`xC$z4=u@<)?eeX!+{c$&{*2g_wMD&7(!ZH0MZHSXd;}IZn;${T9 z9mLmjqGuH7o(cO?tIg(&7Shgi+9SR=wzQXj;Qa2|DIa}j%ox!BzmHO6=aZ0o9RRDR zl1bE~u_Bl|gD=x&U3e1G-)V@(;FekyzA6+l>36<+-0@v{{W4ZP7*e-*;a+3v)O&uS z>-F{{g0Fho|8nm!RHFF^AMyg9`uuq->X^TtM}8X%FbjXeBau&@@ej~ZFoQ?0B5CM(9U=opq_Rj#X8FLc48X~jVLr$9L$<9yuuXKNRcU7e7 z_)C=Ftvv#@m zJ@(6xDKNou^a><`E(Cpe6^qManOJ_!cy!_kS1$IE>C6Dpe}IVF9IL~eej*1CcTXDb z`{eIks)|0XFnwKoGZ=b#pFHmWJjCs7>Ok2vkET+s(2>2Gmol2xH{^UNdg*({KrO}e zJ?B|Bx)E4Bkp&NI6GHFC#K|Toe)DY*6$nUoioVlsW*)=vN-yD4! zyA1GQ_i&I({?vbYb%gW2el@04UK%S|EV<;b%d_EkA7oy1R;4WNs3y=lX%X|3k$~R< zF=z)_VT9qA9$Lh@HJMEf%s+Z$HaIw>mP1RwO`I4TS^l6f$OwxOoWzFI-;N=)lQ-Fe5Tjt@C1YO7(-i_v2G2YWC%j=HkoV>>Z>$y7blb z_5n`&mYwP_nE%L7sZ&L9^@PHAieWbB?9NNI#YG zExuk!MzZSjibYA{8Bk>=CD1{Zk27yLooAhy+#BMv%P3^?TyuvmCXA|`nd@}u347?s z2~G2jamZ)=ja9(qk8E=ZonYwupUb*u+Nt($XSur}fQ36;FCAG!4xleM$~@DXCYgl3cVQ$iMe-NOOe6Uj6m7J0HOb#JlN*)dl&^5$JiV60Ay}=kV z5b65x(x1Jc-!m)-rE+avat&d5W%ubee-aR^BCK{6j1G<8dd4a7OXTmgXsuJ1ReGa> z;Kd$?kq8HT2;Nz8F4}M3mtrlV879Qh_XdpG)2(au^mf$kHK0_Fzr6kTIXr+0WW4zqdxLHW;H1QLE%y1<-O%!XiRc>kc)9Z`LpW@7&hGBU z0CY5u_cwb#LkguA?8*V3eAK7E5(-3wZ&OIJfCUCzrQ=!t<1^fkyER@zvu?K?imN(74h91s0n z>WNin^KkL@hgb2hFsIoCbT1X7!hP@`Q^=}qoWZ%zh%+sg;qN=zCv`CNe&GdKUov^% z*x~eh%g5;ylJDURyVNQREG!Yx6eGsp_Ht$<->2($-T{=j$DLL`0W08I-a9TUly{*< z`}!sEn`FL=8L9Gr;a*hAcdPg`%f}6KkdAz=Z##Y4q%*wFx(j2@t?$z({PSRi?XuCt zd9v+6%U8j3wxG)FB}bX1i3AG%tsbVd^fdc{M8x+8x$+RP(jjl4??QoX~bHCxtD$4rvF~GUT82`eXO7v_co{RmEwbP zF7o+z0&l;o@0_yFxKk_-u*$wkD?dxtq-c>9!k49_6-!W%g6_7xGr=x;x zCmTW0rglqsWOr$6jt>oeEmmhCv}Rc1R{Sn8&|cT>%Kw)_5(5POOt`0NB0L_BbG(OT zUV2uCZ|4m9=|1B#VRzZ#n~)-0$yI=!K- z7C8+_JR3nDrprgnf;@r=whn|s!e->pSf^gE+AS{`hl|#V*d)@u^&+J-T&o-!RxXrt z^}3k~?po$vmOU@+8sBlPOlCIv%Ca{|M%nz*oi`%=U{A7<)jOkhG8`@LDjB+D{>!CF^9NO<1zLiveF zZ(<$K2Jqw#NiTa?#)Ytqte)x!{zjFo*R^HW)f+hwT8zsuuB~T6k-}#oiCnaNsZ@jz zhl8wrEq&=NJYV-rY)Q}8fg=K7{F;u}j_5MF%R$fG4A z>$T@H_sL;U!ubpg%G`T>P`3X>fPnNqiCB9bpEh}zh+u_xmcPB}*lL4}lJ2|xXE0Z| zhyC&+EEFlvCk0~fX~s9=A0Ypx7gz7ikk{{8jqeH?U{82F;CAb3 z6BifIP>j=v*V=Enbo8G~S!D5s@YXo*wuTRki1*sAzR#pZnD7wf4Wv+=UR2fB@?jiHWi}}CaBMMPnf*L23}}U9=H{K|e|@qCVs4y# z-dPfp$Mz0hUp%AlN!5g;KYxg^x1HvV?y6_Z%F)R@;QjuF?^FrqsbIQr;v2zb*E7x0lfToGCt|$9;pETxbUx{}6a25$IpZ`hpKQwZ+Qi8uawC!;E|V7Acl*Ql*M7RlW(6W=bns zeI=Y|?)kt$Vg5<3 zy;2f}Dp_@Oqia6lv!2xP*B<9Wk zLf$8QxHeFe zG|0lQ-~9aS{tL>gcYc7k@)WQ|7NJ%6**GYPanxPaD9(Whz1+sADd!bc4x!&llTEie z90D%WM;q-%cgro3jF|a|D+cz_nT7WU_eOnVRd9^`12Btu!a(*(3sRT6r{Q&QuQU^0Mv+=w zVud^&e%eIFTnt?KnUy)B^?qs=X``LS1t=EG21(&yS%76{pm%eqrz;)3@()moQhQN{ z=ILP!X6D+vcX{9>V#2Yn$wd5=K8KT8d!G8Nc<#o@99KLOBvM;7pcoj#9l}j_N(<+v zLyX1RFEOf=npfH2U4ANwwFFXKW;fTOp7W_NyvAprFT%O*K$EQkGczcoj_^(`CvJ?F~u3HoC$~C)_koL#9*rf6Te4iEd zyo6bAtQ4|J{jjcmxTwT=zRHv=ic?fGzFJ5!wtTK+(+SNq=`c2{$a8(DPa*lOvn|5= z@|yHMemLoC7)BvNwr^jhyZ$m?!+N)Z{4W*L&eW0|UAf$``!CVA&@VJQ4ZHQNa_Oe@ z_66*obN-hSzG+)#m~F5`Lls<>r(HT0eqOhL4N|ao+h7 z_+}Jz`3Dyt_k56hf9Z=CWOh~G{P#+CTIfmKouB*jcW^Ws2L}Bd?BV2x!hGP9E9CZi zL(&x>x(EL{#O&DT(i;#cUb5JSoTHRzi$3(T$Y2wTx7fs{;{?;$cPj}Gs8<5|ZNXjq za1!wvW9Qib)owIM^R#9Sodtx#`znG7L0fS6LA0}9h;mIivb9fu_gjfm)UO?@nDQLM zo;Ms}P`F50*dtulQqDxsmj-mCRWV!IpK3eY`nzB~w}{KuDY)!UVEudSyxcwttC}GJ1~mU+!rbtyX86HT(5>aSId=0 zh;LxkR%If4S~VoaEuM)#l*nGD!c&^xh11lQ5o&nIS$H_O{ACN=Xd0$n8DsQnp|+Qp zLtEQZc6~f@>d1C!qenD*G#O3UQF+Sup$;0_wPiTR*5gWRB+yuJ_7HSxhCgu*2#np> z{-mn^R3juAa&J^}YDAYiVRI$qv49aF_KW*3SH*lz@nEuA8k9F_s^&EUdvGhx48{{% zN6bx<&*-BSJXR}4<02CdG5y*3LbEhpsGC#iC8RrN`#7^{{xg;iwg32W*%RG zuLHbx46(!^_F}q8Jzs6zm^I;7ijXAJ0|IN$f3%MYfKP+K`aL)dT=@14%4tS$=!SE>N2ipf4u;0-C+?_~-z2HB9a^Y@prI@JhoY&+@^1C$B{kl#=1)4xP&HCo;O#cm7 zE-3yg^;b*Wf?z0)()4iq`>{PsN;Gh8Zk)wWi+^7y*LRwr8|&!(QT+(g3OLM)h0H74 z(&SNpU1uX&OmI|qvZQ}W&>Hs zn&85`_izKmuaxlAI6$xb(6`KZS*uu)jf&d6Z1=l_Zl2$<*3R(W{viF&LcazS_!Wh2 z7weTzn{eR2eydxBf}Qu16sORn9MjeV?fC)HXxunCv(F*aq#b;u2pyL6pTfyd<(1h+ zzsY|_Hf!M~WAjXz^({b!MsCzFm@jFD=y^lGaN;a0KEsV52z}Mhnd0t#gr_LV2KsKZQLxPQ3a zaTg0JOxEco+6!GgCO79y&m}As6-5545Q;=pCO;(@sc@9IG7EMpa_2sJ*LFOtHFR6= zb)OX&^}%>8(mKbG%BE8)cxn=O0h7OdLB_%5J~-1rZz17UYb5+WlqG?}!8%uI6#9Zc zvZ;pR3x}xrnQO@UoS0>X+{)lIYt5)Ko;HP;v5kfg5;|ptM?^YMiVn1|&JX^QajHow z6Ln`pjgrRjlEF6?t~02c;6PG!d)?=qoqO_vw^Ay?SbOe)AOkH z@_y1kJkpnnd(m$6|Gx%#@^*b#&SzF%buWuy){aN*fyVu8CvcKJ$Lb}K`Yna?oV(8g z%q*TbUeL(4x?-1DY86e7I0Qc}ei9+gxzgE*Q!KY{+!UoHpKdMx<>du`7%}hjMSqbP zw-MD7f^iP+lhFZ>~J$Y1I1U<-G#B`DlM2xr*lm}E!nAHer1?xtSho-s;IMB(2scKd60FjJJU}3Xcwzdo0acA21N62Ft(!B59e&@l%Z^3HvO9y0q6cN<9yA*SV9xrP^U@1{ z2l5w??GpZVbtiNf>cA$q2VE2LPq?l!Ykb!mjFW@K_Y3>a8{vHSXU1Y_L5R!v@cqFj zBpWIj-mdGi#E*fbWvA*k|p(JdFK) zIWXpgx*J&^WOnnX4kb+4&}@iA%PY<8r(TxsV#0xBuCS|-#U!(F)}sw5YgSR+@N19! zOvtiZL`qBbL_gc)7J3u44!2>Z?ap>Xf-1;qL2*rh);5$x$_J=(L{Y_;Be3O9u0jD~ zWX$p{2Q46psSf74H^2G*uix}ky57rKizk-a!?dIHA6oO*Qg~Rn=++RH!v~l_WuKtrX z{_zD>&n_}gCih@aBF{x5VAR(1{dB&KVrH%$FPkPP!UwC8x9J^P2>wF!D4~B~uwZQ4 zqPudOcZ)QJx{0IYPd@ix(YIof)snIRX-7&e+@870N>AP8l!$q%yh3*`-V8@kt1WZk zL`ID{I92F7DL4&C-hPo%CT&1x_65(=&AafeRPWAy_>8lp;;r`mGo!3$?2Xs{+*0>M z^7e0Ew`03AyB(}O^ckS=>puX?e`hdt2yrEhv!*l`943t*3b70{vs8140xVY3F^szI zvA>_q*OU0_XyUX91Bl`AM$Met)t%ln0DVms&VJU*bB<1Fyjb5d_a`~bRw#W(x+G2# z<0i~!dt%HE`Uo)?3)QOu`#ZKrD(YA*g(~O_OnEqwk2Ealk!~pZLJ`o`tWSgIa&W0V zD!te^l$v$BD9m0+m^AtQC&h^5P&Sh^78!AkiT=N^Wj zl6BJ~-s~UX=?QmFE%X>QVVhOL`xm}iRz4QSw#=He4OlO6Sr;}u$wcw1G*e(PNB;t8 zB-t%1+%f~7`ce&2;M8@z45pL=4ijReJo~_@2d%~JPHf|pM_B~ z(WSGi3Ko4y`~57_klBsDCl{<{qac!%3b4}#G86c&L6LuM;lt)k!}>`_b4_o=qu8A! z%4z`fCOZ?HnW_B>=C9SzDO5XFv!<1pamNO^@>fkbhKCs_EU%6;j*T1OD#s5Ib>pt< z2wWwXc{i=W@lsVMcEu#?H<~L^?J#%3F91NUG@?liSE0QO+V%fMmnSWTj>f$5Y^uQ7 zPQ*}@0?D7#(1(>ZxjmeZqIBg*acy^#P&Of9hDLK5BZY*H#GZqzsnRT#8h!MWH{6ff zmT3`Pz_zt%MBG7M=6MFeO82i8VD|2EsnmJ&O9P25@tPLj970xD;R6X?l@q}1--tm*u(xysQ6n-umei2@S~&Z71pnfucQWS4~yRGzpcM+j`BV5DA>aMbW)WcUaU zi@J_JL&Mq_nVG`CGn@bSfWQ1Q8C%y=skNyV0WLGwoUMryM+XUXpK2Do`3H@8UZ`yl zVfQp`?T}i0Zjdl8Vf@++fUQCL*%c!a^X4G1J`@*}H4OE1@YsRjWI~n(POIT_D&fsZ&v#o#D z%lhTaXs>Ld(8ULFNhNfcNU`&o6U!~iE=m=m_zs=~v=j(bI!3AJTJM)cU37oX zp$Rs3zvX%@audT{DVIX$0Tqah%I62ufxpmp8yHth`CPDIa)_E;uS-IM2+j+D+vXR0 zQ6&Z!@vvuEZ0^dvG?KkD4jUV~U);n1W})A?ckmk2LU$M_M7IBHhYSq17)*nzP;KeT zQO04{9Hw_7oKNk~Bj2k?WaaBA#WGi(x^G~5q@xu;AVWL3(QXgH51g=X4#|07Md+F( zGa?REOxF&i;Ktbt0XtZ!i@GIC*kD|ySDK{IXc+G;>yLJWDu8)R_xJ_UyF&5(2yrb< zIjPsm63*inlyH;!h&W3Aq^!orWEnX12(&O>%7Z>9y_aFY93C%A`(R?7qhwOs5c>+q z+S`AD%{y;Up_N2*D;BM-M(G*YK^(+rkF$mDo0d=Z^5?(18I*tX_8TVyY_ntr|59t8 zMdrACTXt#;9}?ye#td%P)>EJ3jdOeoc(0Yg6OaxHTn ztcY<_vsDj?thP0H7YeGw@OQ9-qm;LVszUh`?I1 z4IffMQ=Kcpu3<*oqK5QWL=3~vPQYZiV=&yiZr4I#*(sK?iS9@wKTN@S&0C0eLOOf@ z?5YtseN6oW; zv8et8SSRRR7`3a|WfEFVs;tnbg8ukc>ArHve1-+jRCy9dnGtd4n(3VZZDXtDXj1lb zz7}2$##Ms?8%j`ln>7{U9s|FFFSUURYj%e`I^a<)g60))XV?YRQk_9bGynxH%%sav)RL90C)^H=3;KkDLP zWyl&Zt@vyn&P}znV%F6SUX4)ils>P*ybj)vLa%2W&w}!m;J+SN^_{}Ba7)yOK&!|4 znc%qF!eI(J!_5br<=+$2mryy{WsUPZvO7K@AE=c;tIWK^^-aN@6UJJH%(oJF*9Yto zEBE_-nVJV=d7Bku(!Rztw|n7JePH*k8-&;b$Sz}>@Iv*xQ}=t`l2Z+ApMc$m1o}$5 zbqJfj-q8h3x!3aI8MRi{M|c1!d6=sKb7Q4qySeqx`hwdJZ~f1nrAM>P$UN*nz@}csIqRbz+AcuyWJh7u4vjrj)7Yv3 zbk2F&F3^@6F-#Wh*>4jMgw0~vP0a+S3Jhd~;?JQk(^-xFuc9H0QmQSttysHJqPFZM z#S03z&}rlZBzgu^M6RSd!WY3sV{b-N1r=L-OCw#WNE-0>ghDY5R3oeUo2jb}xeZNJ zgXgZaT%usLJR;KJk;~rKKZo3Q9Jg7P9g#V;Ga;8w)Joh1`G=|I#jsd-Vjavm3yWjV z5cSv5|56Lp%#wT{Qs^6v7%Aw);v0&P=u)-giLdC~ zN5U@jbzj&ao^Gw`w2;krBLbk&CNs*Ou&T5}*(?f=O3t6H)gNXERS@U)u0)pX222=Q z9p4?~BFhLx;JCMe{7WPwABg|SR1^lbS7A!tfi747f>05f&*(Lbi{{*AHyp5MBaqSG z-M>O(C7W<%p^5Qj@ZdZ2lvz$X0CBo!3`jUk%ZRl1Yx>B=Z!5!+yPMybcnNmiR=r&NWR&eU!~jlXXI>P zfG%x$DBHHSf+phn)(66AlqB06Qm;U5pKt!lt7n`vzdL~;bEHwo&Cbm2dKQ4NQ!B@N zg1s8fS=k%O>)Y>2p9n8DM1vaQj?-0wq-#$MlajyCc5!DQB{V8@hV8`DtaMwGiQPLp zborliQdoF8I55@cPl0M{uZ^huhI)by*Zz}ztZ0RHCy>(;aGmR45k?r1kbf)Rk&aGy z*fRItBA~?fgzz)JV#ZW4$_&k~@SzJT@Kq-e?l~)neq|^jZxb;#&U!ew)+^g^b+3@3a!lr9CnUn+a%AE^-2#fJ&fL<-BcuWp!dnyH(-_Nmc{DCX zpX87?w6)i3&AKI`8?;Fp?qm!^0U?|j4@QniT<5&KPnJj zE*AFoLdmaHoWJy@Zy4KY-&3`7jN9fjd86Ty-N<@WaB9@ zLOo4vp25;4nathpN+ez7q|}FzoQfRRGE1z#!uRWkOlTQUecUd7j2lp2u(c~&?jST} z8hN%9T{~*Z3A#nYLjWyPMurHd*ejDBg%d*EnERyfmJ`*+4Vmq&CV?~GS)c3 zf`A1blY!P*cDT0cZOlk*7jy!7iRBZD7O(7;m7sDorOchNlVDs^pr11W^_pK@HJEpAi~_a8&n5 zfeK)_ackuBu>JP4mBQ$>$5sE>hhNs}bHbXAaKAPbQYj1uU)AM)uN}p@AMA z{xrKF1am8K`UA7V)%h-IHRD#4VQc4YWXG8#X7Pbdd>2D}cD+@LV0}skK}_q`dlf_A zSXhTAdlkzk%M7|VbPUXb)=babXbD$BMDP!wtqe}uC9Svc)F|2-8bb#Dt?@xMIUz|LC=0=GVnV>uwAV&QRg)Ba2qV zy9GLxz7pm^Y~OezubH-Ht_=Fw+0q=Fc#e?sUp+ef6atIfsExKTZJRT)Y#rclV@{J$ zrWV}vM5e0WiAbY^Ee_lI1wz4LSnUCvjT7%pTTr&AY6N%~)b@z4?^tTz$<&ulCv98H zd4CBUjkJ_B40@iT#32)Y7mc!(t2Ro)j)L7n?%bkZ80~-(HL@N(m-{8`!@Av>s++AA zb$GPbGg`w|1Ey( zY9YYpNz9pGYI?hMYfmT@uj)J30I0+*bl7?p)klFKFt+h~Pl*XKu{$)~Zgv=Ak-!~g zWWHoioWR|~Wn4Wb`M!dRcIgkysL73&mJ!Lx2Dv>Gg)#%eIz!vO{xq)586I&AZT}w4 z5iLGU6;d;a&AQBwr(cS%8DLRgX$zgU#f2gR+DJQd;XHPFEPv0GjExR+Vj0BUy5dhX z8a0XOT74cn_EG_s(+$=q&gqpNne;V=t;(GGetRJh2`wwWeG?cI#GK zA(JY9?4U)$Jxt34YYZHmgmQ(yfxI$L?{Rw-`l^FZPh5_qCS1p_TV4Y2Y5cO$Z)M~= z8)edOfp;6Rs(jG806eWK=IPrAL)@DJ||&+X#D6G{(gFObN%e23CN zCNk1x!@~UhvsF|^_pzENJd8b?MT2!w)w2$q~ z>^vScYKY3)_bU88=mNeoTbO3-)N}S=vScnSNWX%VqlAUYZtg8#$mFG8(21zqKpI?g z_;jmnHi8R>W4998HWUY$tS>%PIg zW2RxG9k2scH}fR2W#PXs8sB=j4|nMmY{5K@dF#R=x{bXb%}D;@Cx~AhJ@eeR7S=`@I%|sFd>7~ z)qbJe%23!oGB$ls2Ha(|ick%9H{bjL6W&KZd?gMGsug*En>=I7*JXj+d!|6WY{6m= z8;{9zE~dd>Vv0#sR4iF_vp)Ocod)M4AB;ArZDo$Z)i>mxtfu|g5YcmiPwJh6<+&Z< zdNlN~g}!fEU)8{;0{?u-RaI1wN3yu%?D35zC{3LPHeUZvdAQ9s;ai@c_(Ahd_r89J zk34)Lc=kqStZ7S4BwA_GVUwb*HyPRN8IAG$x%cNXhV4S6W2jg~MS&6Q&dU_~?@!wD zT#*?QSRq95rlu)tZaw=U=FC zo1^E4i#JtNgKQ!8Oha~*Gf^b&MidrswFXd78GdZ{<*3apT3Df+uEFxhR>a<1G?Z%k zTFUoRRzU^C=R~rq18=zhQz%)M>xEfWOl!kk7E6E zKihk<;i)WQ$crCMsfDp~1&^@^DeO%aHW)oow&wG6l-_E{wheI&r#uDjxHH6)3Zh4t z_}o}^VZtD{FF7>$lQ&h?r!JAp}W&&`fV2>>Hu9 zIJD-^aDu?jM$_DI_ISBad}uH-ArNjBICk*Agu5E|96R(8h_W+6*4~D?whz9r;ZUS2 zO)6MMp&zq)hYX^+9lu_^Y~syFSQ47nANMFF7Yw0)Jl%ra=O@l}%^NVG*EEO?no8hW zp6<3+eJt~laL4qLjKg$;Jy&@C4s}dW2r#{ zh**@~Hk2HPK8XFN?T37q^7-r8%yRwLOdbsrqSuyil*S7SFC(|6gZ=EKVyl$Hi`(9 zg>NJFxHi#m4+fvRL=x&x*C5hb$sLzntffwdi0oho*>YA3U+cOALY{IS`|KDdNd?dM ztdZdlCOvs@ap*iG=tTs};%Vv0e*9%4as_TWKAl||Jv(-sdr0+)LUFCd+qLFbBjq^_ zz1JzjllJ24$beU>A66206yk})eSzxJhB|czDL_C?^lY7IrAFIO@ z4FYouTAhpGU2&56M}5#()aykCcPv&$;cMZ~eceV=ja4Q5Sdo{)Hq;8%ITY*hW8}@# zMfo4P*(vK32Wudbbx3+$94j+AlfpHBaQ{$+Gha~!)8_W>2_WNDUWW!|Ci0qC3>e+} zN`)>?odkjr97!GJc|_L0wwUR-iJM@}z7>L<-R@s9DA1V`$u#AyvS3we-_o8Kb6*fp z_yf=Mdp2Su1g4HNA3kKC6(y9RM8YBtG#cJ7>jW+1uZhqwrq@&aAB}uLADNNImG4B8Xz(E2a4UGNXrf3lzdS6jX}$E3HrS8^3Oa8<^0ypf7XE#s z+T__*kP`i@4KY>U3CJx>?ibC1&vLyX@k$F!R2QYg@%AOruDEsMi5-zdL0e~E02s(2 zAfs>2d)^FhBz!^Un%Awj4SlNcgyMd)gjP0RhRa}Th^JxfA=vg4qDXT1r0u5;^|J2k zF^02gsVSefMpbSe!Ia|>bd)Hf^KeV4JDxE^S=IN9XRXIPlo?{v_d|m^$e1)Os?Lm} z1Gv7})E7peeIkPlq@c7&3G*)aft%*CWBe4E!iF4m`w5v)p>kK~P|5rspz0b`V|l=y z`ONfZ2(%gzSEZau3wf|t(^fp2moC-PpL)cZ_cZf){s21Dt6jq}%L>g=JnETefw+{r zwh*7v7=Y#V#hJb{MRKk4v$cyeEjEx?Iyzcc6ZRuUj$^TEcrt zeX>t{4g34q)v>Yt17=j}ua5NzQJwxoT)S=l9$P0Tq+|G& z26$aF-RJeAp;NjSm0Mo^pkW{L&-Ju0yLY5raht-RXU;V2b*xRgLk$%wWvd#>jS9M8 zJsy86OX+%9Ld}AxDJ>j~0*6EubaXw9-)LN%(|beW(dm|MlM>sUv1WmGPA}#!yLeaL zCwoh68_#G+&sC34!|;I%8uR+v97C~=SU3F3`t-ddoCUje-Kwig#7Tt3mkU6=?+^HPz%?bZZJCQdk$m4D>5`1 z;$7AENLYoUi6po}@uBbL3E5ov*=twG33`=J>S2uCln6JZ+x(t_GVMrS;k6wk)=FUT zIXrxkz#mJ&(pHZy_w{VJexYBdxlzHFf(&N^r4m496#FSDJu-jV%^tV@vW_{uv2(37 zh0tgOR}jQp)0>z<2>aOxc{qR!>>e~! zs(EJYMb6${NLfboyrTD-wJK;GSURs&4bKfzWkl!w=F@F?J=T-QI`SshY@ui?5I8;5BxlWw6K zKuzVClI>uaYEY6n%(YIKYjZBoh`uHMZSI1&*AJshl2Vt!s9DP^+kb%Z^#SRmVy+KA zk{AT<>&8o0+gCwa-Ax9Pas{+|y;s7yX}-RHfH$C<%5fh(I|VMi`#0 z;FC*1#6nk)r@&>EcTWd!n?(~@j@}ny2lpti4!iU?hyeM<;ha*(;%WjJjlHdm> z(yx%~bBrH>VcID#dTsaf&u{`{V=*rYAeL_24@9yVNjai;tX6(H zb|fGd=V|=C+9k${zgw%ZPtbp1(d?jUo}{KP_Fwy1~<4+P`4 zoLZ{PZ)&2#GDVS64y&g*-fNvqIC<&rtU1Qey|E22l=_@D)qIM_ls z{%+G9E4~nNBEu7oXqC=d3S-3&Z8wr?U9Xwm51%KoLRCJ8cWH zou=-wDz1mC3{r>Bbr_eb{WR4(0cdcwNlf{MeaIEE2iOOm!L?W-C3+P|B^!`I)Qn9Q zSV@fWaQTsoOebBm`}f?r1j@Dku=7zs6KngV;Agksr(k73bF)^#w2{&JfZ9AuNkB-& z&1sOUKu;g^D>a(-&3WIAHb|pq!&H*HQq~+`MS|_G=+8MbHW8X| z*H2aQzug@14SRpGh|X)Vt!cRWIJAm>j&?3ZE+WuID;$;7WJ+Wv>F4zi!Lsw0-bo8t zU7RrpyQlFi4of%;gwlFUOB{Lm+W$^pD3^?N%`#VmyHAWU5fzLGuzR>R_^rO-pjvR{ z718^Zu~IkB%1jV=fX#Y;ys(;sf2IEpI|$(cq@XYgqz&3=%(urvq{?EQZf13m|5C+- zXipyX`~tM}9D!?(_gT|9z&W9W3NpJRIc#)1>9&frpYOeKjx>OWy9tJMz;*R9TeVAw%TeT6It_Vr2)z_Ym!S(6L~45>*%0j$T?WG z3#n$AV&KPize=lZ#0VNyCDUuhY&P~O1a~Y+%j-*y>n+f{4*|$fg2PHC(JU6mMv6?s zDm{m{#qIgVl%oT~Mf5nvjvRMj=Z1Z1c{VyXd^O zMsO$pKkD8(sE#J+8$I|zgS)%C99)9C9wfMXa0~7nT!RO9w_qW-yL)gA1P_70^PN2M zzJGlG-Ktx+Zq?S-?(FVN&rDCxOmFwE4Hi~Y_BJoKr`ThEOjAttc~*TeGuplMW{oaa z*|%PwlbQyV`c1>XfE+y7-MHz`yXsR7>8p1A`J})NjB<+gS9|0+T5O5mOCvhL_*9^ zW_-|sK~nxe@FsBd^!@3;Sh&evCPp|1;vRlLLS~U??5Ddfb=*e+@>{ucy^thCa_TQm zzePcVKvscK17CnSN;zU5(U1y=JIE_n)E)+MIK3LV(iN#06}E{&LW>U^DJyC%OWTJV zV0AjF6Y{q{ZdUnnH@f?-boK`kZUT~dY%yKcDE^COtiF1BltIhS)EI<|RmwXejOnqPK8uM+u8)j5bhKOu)!W8~Q&Olnu9 z^$MBRY~k&lZ(52C;`suJyD8B`uVrZBB$HH%%BX!$!}1kwW6nz1nGCA<8QFGRYTgB= z^T^wp=RDVaaOkVyb^B(`@NJOy2O1k?H^BwX{6_LkVun)cwC=k@iafIxLCg;Va>b4M z*;ZSuB%UR1Kwmc>C60VD@d3sOe5pefie|df5kR{gDMnBNeMt)VV&uDuJn^TGYRRK` z`rkK_dc0~b!exJ1{+b?DDxU`&VhhZq^olps`RSiM?egG($_?G-Jvi0OgR&^U#LMBm zk8gwvb~>OSocdIeYWcy2f5m1oW~z*!WOHemm>ee4(EcDd6r`nWxl8_Ov8mf*6^QOFA^ZIU}^mG(>45AbBPn46xyeROtZC9^cDHMw=pu*@BgvVzZ;UnYEC1Xo=u zyjJM8s`#-BvPx=*grMx^Xw>55`XIHuq1A^P>;F142%Fr@uCJQgty*Nczg|D?0_!;P zgMlPQ0^#f$3$pmDx50$E`ZQ4_U?~*v*7z;S&+LtcU}8;Ol&h3b+zRAXlD>B@m&+uX zY)`J9as_p$E7gcUk2c?4$oF3<5Ljl~CT@9;fPtLX6Guj8-w~`x?p+k96+g~L;`d^Q zWj|cI3RFK>7i&m+useM#3+_b*ex!NpsKe@lM&=&m-aYOanT4~#^R?IqiA<697Y2KZ zuU#1q**3BR#^RuKUbp&atNt-X8`c8T{v3p#1c~VL z6YBLL;43G!1y=hpW0JJUezX~s9}rK5?dNKj?J4@^WQlaAsH4F-8_~}=7DrAYf$UwL zhj2RR3y59?Sv~@9YzIwLj5mLN|JA{?M=!A%os8{rlQT+jLLE=eTLSNd%YAZ_9cyQ3 zM8V2%PA-ts*}*|mBY!kOskK+h%)wb82A-)hagytKv2HCCFdEHyb~JBNKrfv8{b%|@ z!?c`0$6J)=yj`K&DJzL$D|Kow(}r5&V6SSSlA*u>aYKJjTK!4P5MreFg}ru^t=9P~ z%FfT?!e6uux!n9Ey(?~OGszq0K?V+2+;Vr?I0J=BQ3Z2te0(6}BLiM{vABc;QKpGi zBCUvVpWd|%%fgt!cFSAOAVb8TSIsEQX>TP)-sz}9BPmS{!UX#flQX`9Rf=jkfG-IxSS zC?Ufm*D607!psRj(z^&oZhJ5ye->&uQJM?7BE$&EI0Nbe=g21u&xvwAn71D|9vyDP zmz!00c^f7{?NvoZ5_3VYWmGgH+O=C4>gmx~Q#WAvWAlh_lFL z6lLLh7?aH2G9oA9du-}i(>L&*$%2fZ?8S%I29iXC#}PYxJF)#@=FQK=cDWWML^45| zfsqc`?+LTEyJz0x^)2nYM# z@7NLvu2uI7kjV<%Z25@y#3mjF3usXKM{^D@`?q@*ow1s4>^A=}?eN0MdJ5(Xc@nh- zI1q6Jl@|ufBCy*HI-&dLQB5??Q=&N_>cj__kQ9i{;y5~6%{}>Qh=Yti3l#V9jT%yH z0DNcbF*uxH&O|}h_cqh9lV9RoMe~WoU*g~j^mIh|TZi9A)~jGL@@1p2pcAR3^qOigCnNTPS}K5S^*yH~RHsA%v}bsgRiS^qUfQ;tH)02d-o_zp36m?m(8u zg;&O}KE(SN{8U>I+0jqbZ&$pG_@=KMs5-!>OM0gIx5+qE6et%1K29iCmYRp`(qStU z1_{SL$0XFX<{tZ0E&WBSnOchji8)C-ydP0)O(pN{4*PU|m~Q#%xDna8j-J~Z{ zkrV2`m2p+%r;Qa&%S)iPhpP|bev63~roHQOTNA#k5a}D_+4;3ASNw$m?_o#mg;!y4Hv-Rc+L>+fZ+Q#R+sE%3bPdKUDQvG72@`h*YlJtu zgUhuuy~Jr<~@p5L5@?(*E7^;1dT0OANts&}z+Jq_LKei#+u zkwqFT9&;q&sld~;+2N(!!!NzQ#h~FN-(@v+&INUHFR121HKv)@JW`V-l9Cl=!KWRb zv#s5xYE0iw37`(eLTuYcmJZhLQA?LN?i7LTYGr$YpumcoCBWOnj+3=gmk z9K%|UKk!V|xZ^k#?7VzGbOtY}L+xbjZ|2wO<4K0{u+Dn6FOM*ODOwmvOFP)5c?+}H zUlvbs8#yChFFLN5(dk^PZw6P`1O%We5H6?q_zgQv#h0Sr!43625^~nqQajn`SqM42 zxj7=V^N!r!$)WSb+df)W8gAGgXaq2IM8W^bw%i47D+AOvXibGKG;&Ab_-n%Z#ze! zy7s80_4em{71F8luc~uK(<-)!lZAoxUNrP_ttk@i=t8&tF4Z&SVlde-m%OU(lBo3~ z<&=7uYiDEQd(Mcw@S~|3OUY6BSFhf43BHIA-mNeyAV!g>)tzmPz#odNLj4fNOu$?0 zAcvWY^kG$L_-`4U0v?>rtNW2?KfN>MRLtPF@|kFAO_VWX4J9EZmZ4R0#D1+8D@MCx zo=Cr;QLNwm{0p$zT_n&x=?XQr6iqo*BnTem8=Ly2z4yl1!aK}IQxs9tE6v_z1Fg&pn(5-K#uU*8lJEmV=_VTRfLYSNV zy+3OYK4Axr<`aJUAr__AiLS`uV7uP5L+Udpjmu?il52S>a;K>Av8pn(E z2Qv;99v%UT8wUpkjRQc>@BjovENmQJOk8|UN-8c1bv$k!Y8pOCTGO{K!5V*~$HBtD z0HK2RW2!>x41d z+Bt+bWIefckEn?&D^sJqRh5I`Y2ho9)!0%-mZ$=H}dpeaJ4m0$gnE8 zRknpGyxPEg8EeeC6>qG<>9ndEjpx~^(a$Ek@mV;wf#Y+nO%ZFHb3I#D){Bn6;5Bhd z3KunT!JtHX7s`9a%9VHsLab+~V`iFt_5RY3{Y_;|EWTfF`wSmB7%Qr3OfYWbn^HQg z4tY!3J%bJrJGM{(w9n5R^f}6eN@VnuKWl4GAiH{j=s8<`-}Ca`hl=?y*fZJ)qTb5~ zsjFSI9S1=F%6&&2t~Iz!%mN$Mtd|<9F4p^n62r`O^?V)anb=!?8 zB4mt=hZW@6qKw7YxpRJTvw^9Gi1=NtiTmiFAt(RWYPwE zYh@v5yTm|{pL~7k;S|&?+zGF$* z>%_+VtdLuBMj?~1ulk0NuN3+06VOaz_GNOwO(}!bKuz+fyK-z??^3nUV}1_TO=ukq zi&z$v9@jM9i~!^6X*H{CS2j);U$=OD3Q_IQ(^dMlXF-9z)>PqtJ2B6|h2|skrkWe- zgNG(qkC(b6W^8vj)$vWPwY>}Wy3WyX4A_#<3`T2JpiFJz2gXvOCbFY9A!Ad1(54#V zUv1>XGvrg;7qmgYu}9d;Kj@zzKE!mvZW?PoqO70HN^Tm=^jIEAHDMq4%z%WgOT;g9 zB_YQx6{XBjre%?#3LrJ_$q=tv#-XKnDTGVq)(61FNfE$!CyJ1Y`IhPEU)CTI;)hB| zHU?UbE6z+)o8ylC8bRJ4ptTm$^ujP;&#y!+TqB@Hl&Pbl+2gC}_bpN#Y;x-SkEMM8|K{iQFT?TmyVG zW%H~&mmO;TzCiu5K=xM#2;0mME1F6z-7Bq32MB`8TLF&lYa*Jl2oZTd#krn*4T{_JF)+0 zR<2gVF;;CHxhT0}0|C_$ug*kb_= zz9}-Xas6pGf*9nRi=!j<4}}$;in{L~HDXAtR054L`nC5#J(r(YH(Gva7CA!w5`;NP z3tZkJRfu|(G|a!a$2mp*H<6H|0U2lI@fa@w5R7#)&hL7^cZc36ug|2R;mKjw`fGJW^b zkpnyCWPOA}NCOVvW)A~{PRM8x?^@j$-#%-9ZW%^7YK&ej*q|a4;q|fCG_at53KVXH ztz65GTZx0AkUY_9HBBF)n*-F2Th3SpL@+!%*0R+ws@<<~rI&|YHgPD&wK{|zW~+ng zQn*EJGdny7kI;n1LN!^=VpYxkcnrFH*RNuSxOefD(qNQfa^p{?>@OmH{^PjyB^_D# zTP~)wR2}1n7Ru6nW!hkSTEl70vSfEbrBZzf?)kE>XH>W`1(|H}m6F_@rXFK!WeX(A znOr)aj4FL9i010ZRga7+%)!@8bhIjHmOEt?l;}MQ+~(@c&~%-2ln!5=vWm%2&1fpe z)CC!&1rGXjPpfeiIiBD<`&Jt!S}g^s^`-t*$%p@>=|f9T{P-lof*@}TVd24NGfiu* z(n*;F9>so@RJcE~PVP$9?y$c%!N9R~P)j2TXu?OC{av$c<%)(G>TuJn^6Ct#y1=Ce z-%H7k$|_JZmsc4!KlA=LWQF42CbFsl9Q$0%bIkcv53Yw<2ox6Vr-nG2>KVPIzhSy` z@#~&onVo} zy4|p;3F4#x2^(skvb>;wvdlgCXQp+13mJF>RF$%E=M8bu>{$~?m7~Yyd;TNmD|T%S zd^^)JZ$aVM#7^l1nv^I5QKcqC?PO%b>0cSr-O3sX8nRMwNeFh?kCjgvoXOCg5JF)W zX$t>+Or=3J6xOW~yhxQf3rjrHpkB?TFLqa4Ezz0; zG>aK9oID1sz_|d{SKvoSFYivC| zxbO=)*PW*i9~_P^T5bjt|4>z*QB@z?@Sqnn7*6D+r}daZUy=I+XTOwK=aJU(k(W`c z&1q*TB|+k2Dfl){iH)+lJx#KV*@=w@T&q`m#KzysK)Ini@GoC>dqm_&?%>{iXfy?G zmu^rU02sT#{x#H<8Ii3Y4&09MblQFd@bwS0NF z^>HRdBip)E0S~|F#XgclyJR7lbW5}W1FTF%-QH-&7nLcJvQH~x!?0eoY`2WJxf)SysYK+YNPU8borR1RzpJ`AEN$@&pKSR zoi0o6G$?h_XJLSh%<~eYHpEMeX;z1ek-xc+K3~x=p{t=uZ76hFu>yzD~n_Ixfu0R7b|VT;M5x9&AWLHucAep1#6hC@5JLe4<~9H?{7blw#}iAL}P% zYN0lAs+!5cp73>=O0DUfkz2F;e#ad;mBtDrW1-}hsmhWr59Z7&<9@?m)!q?z1qrIM zcMe6bHlPO`oiGV%!jOP1sf|vht&5B6a&W1#zx}KS%SG0g1{T63xBLZoX>47Gp{=dx z0W4hIE&j@{IXbZ+F((%`GHj)7R)4Qp{067Y&{i^Xd*Pir+WIg`zjE8bMCk+lxt~s^ z@8|R}3GU}7yv)(5mO29wch}D;A+qWwid6#wT$4I@gHHAxMz9emVXnBAv@eD5)8}?I#u;r^=L3#yig`f6O<5vo&-qJH5j1#?Hf-(m$o= z=$8^Er)vdMF3!^(SfyngEWFh+=xhET#*t;xsp~LLmK_^|^<-8|kbw7OZREdXO4su| zDnj4~0PYo?h;SBX6&z)a%ka)zjGZKQBJqjb(9%wX;{Apff+~1_i;hTG#MwEs$!UMg z&|9ip5q$T~PT=0s!(FFqZfPimcnUT_@aYSwR>tm;;S6v;$6a0V31Xwpjb`m5>kxNl zVv`VEmxnOyOz)-+8b>|V(R*~(Yn|OAarI9_baxRbFVENbyH&7mW;xL5G^-SeHY29x z72&sM{wtpe2coDZxQi=7KcVFCq|dCgC6hn zf6NqX&vfrFR@P31Psu>^JANYQrV=3zWa&mx)q{+wJk+pqQ`uZ_LSEd%!IWfB4}cR-HxZ9+U5{wqGC?enbpUFMg~3!VCKvW=2=;TDR1Uo@~C3v z@%S7H<3rKLqh2JC%*@=SCz+%=F&TAkZxUHw^y?P?P3ap^M&CB5nFr{&f4Sh^(3L@A ziA`*nr>XunlE2w;sI@9G`$;X9pQwE8vuX#eeN%+& z+j~V%)eYd?*{pIyTJ`(Q)C4D$t7k{kqyOah8Ck32yzDy+iQ#mg@^Umui>owhXOiRw=Lj!&1+&LV|+*`$3xx8g0iqDu8c zK#AJihV4&hPkU*V2K<|_r>^-;`$FdfO-TuK_4a)Z5gx3I4Z#VW4YdU{F%AZ2rJ9ge zVjsy3y%{54Xs9(GVDzoBESxqR97#;Coq;)VMFLhQIjaxg)bG%%L~_oKh<^@i_`NgA zD0296yIm{36>%#An*30UO}4CFTY(0?lN|t`D6bSaxl6!1d9;ib*_gUytZ$=pQopN>b!3+wHVr?Dxxy-!Qks?dhe%+~U%PfMHi_4TqeUuztsDAv` zpZ~x8voeI5p5EMNaI2ZC-*nXHDrPs|to!$`qfg>Y$Lz)?u-t-sCT0VmfKL-6hgY|0 zZ`hILX9y!n33$o6&=R!57|HdE4{em2!!tha5B#x*;@}|Rbynfyw4Y9JKf+*Kgr@9woN;`Db%%Gx!4x}Yb#+1W5 z#FRB&txUVQR8*Ua3@@W(Gf$wUn}QfNPZ>cPh**nc5So^Wi5v4_51ILC@STA8Qh zU_rBn}k&B<4NX=s;XaYyyLE3TGl;kns9&JyQ-fSPj_1&#_A+@xA_Qe za`NSW$3dX5;#`hP!E~+FvNn_PR`z*Q$2*GFS(Cw+nm%%7H^S5;o00!|E3UeDv-uW< z#K|t-0qcQ)iGKYTNu^y)(X{^Ke+rlD4L&$u68*R@_%@U)GtS9$6%%(i^6oyo zgQ|sw?D+hr8juM?2&11>hNMUn+Wm$9?WD(ABsECu&cuZ^wMFEZaX?Z5<-)8-a(?SsDruHWe{8zCU?Bsl|Hcu?V?3!n%#c5={|0Sx5iowc)!h!0w1S1Q&Th-F5_5D`{WR~}|)tu18 zj;JO#0)O&;HsK95dT1R6@LyJ}NSOsLumDqw3#Zdo zr~w_zL&b~c3+zopaHy}+f3y%4pTW6x2TWz_T*x6V%tq$5yM4)n>IL?hHl1YeapUJ> z5-*2O%DILEROA>upqNh@omGDUIt{+OOCA6E1-hmVlm~{IM#(He2hN*siQZ7v)Gwjo zS(g+kYgyW!AAfF{@3YL3TJ6@EnXbf}|5jf8LSS4sj4f^h#gSz%&Ou!Xes)3GY~=LW zb>{W+QW>Ohy#DK-tc4}!_7GXWUj5Nc4xcO*ZB@WNNB@yQ##>Gub`xRr7hO;n+46O@ zACSvWC54Um&q?~9My>OrpGHS#kf%CyOVujHL!mkOOX_@o2`k>_C^#XQAZWc%c4ZTP z&)hgbbhW;4ZjgZ9(U#w_7qd{?=Qe)Favaj;thO@0?D~Xvu)Ekm(N#2=PWTeY2`jk6 zGS5LV&r9+s?DhNQt%F#QUt5*MFM@(uw0PxfdU9(RM$K0)HZWo@0 zC$#_StwnG_(e=ODcVuy>_INln98#Ilh0b0Y z+F#}pa2PHzEo^en8eHZpHI)0*PACZ)e)OycT#wRy-6Z`EV{F-mf<@;lPIbV}P32vW zBCBPam(dj9NG^+}vY8)ZuVdISj5&%%(*j&A>s&f?oGYoTARdst)q4;fE6 z<7*G!*RslDZPsBq%0bG>)?-yxNqqO3aMHxvQKr#x2@W=|meNzaDjkJAMSD@yu9~87?^s7ce-~z?_WL z$&tuBjjPTXlaqLKn~2+@4v{tmTpaHzkKSbY+_@M|&`|*X;(F%QyzlrrrtPfbFgyO`-Exp%o~$?nNBJXl_JGnPD1sI;t?Ti zA`yOyLo>qJDn_nxQt-$DM_ND;Bk>3yx~bps1h3gI+m+Kf`oJX<$DpyG3P~Da9F6lJ zwkh@tp?yMl>h@)x^w+GbtJ(l7krXm68=_7t&J@Y^kxA{ybw?SMA#4K^{laXsG2Y~7 zWi;y$*nxWPMu)Jk(E>NjJ;Md^O`9&+^vRRsS9>+TQ(rCW zxI!^OSyD2|14qcE4ZGad@8dBqgqze5SDs*VcN}+M2J3-qS}D(DbB?2wFqFFyZ{V#_ zn(gyhiYat(K=80utktD}DvW}I-Yfi+ z8g0L@)Gg>n_xqOk1$$kDF&T}XsP2Td_=b$TzKs&gceiJfIo?#nJ)}vUjV94$i@Jx& zP_CgjxF6^VJ-`^Kzd^KnY*adpA0cpt>}9m{k7N$GB7vKoR67h?@FF~yZ62084lL#* z!)k{>9u{k$zh-=PJv3HqI{Z zSJ{*#>7%;}welvoe<>%?As^KSot9V?Y8f-X7KKEjw;$du+XNbLHK@|ikP zp6jH|G!y{YWZ!G!=c}tMf7@+j^B16saHhL9$%VEPsuXSFu0P}jfuzaZL)X>_GE>L+ zgHv6H-T)Kbz|%50phdP!%h_)tRc-ju05w}9kLoRLjA$LlTI6JI6swFD3B2EBV9EBK zHcC})Ei{SVA$TfR1_OF|$}k?t>L8|_?Pi1nO}x~RVFaztXtXC1uJv~|H*jqR{ga0US`?U zqL)Pj@pST_+rmnJ+V##DdjxAVd~bWe`CgT-U@KfkqxwWV^w)iXZe)9c-C=L*N>;mK ztM!EQ-(;0naQ0{#h*El*JVuizBW<#*F1NdlTa zk28s6#2eGe-eDx3W@J4#O;aM(is}5R{h-^V3BP0XYaB;Q5U1|(&4|%unC+$y{Mo>|!oDQaROHk&qFp#cumR;V@QVmR;Phmw zLaRVIKLacV@ShS7;Z^Gn-;w)1N<2zB7utKWN6NlLNJS)jlsN87&J;F8M3r8vkb+c0 zuUGPokMvPd0LfF$k?>cfqzHs4Y_MGWd-cpyF+#}PAjqgnfHP1dnQ?P&u^2yF`kHC# zDv2^wftpk9(Ed3}+aH6S^gUJ$u0P$S*r2@@>jq+5Ozqw)bHnm9&o<(Vy%cdk>i>4}I9?d~3O;jd8;~qrkgr1{6 zk5#Z9c0woGhMd*Hp4EBp@vOK`!tGkZki1qyP6twYFjaVNKgp%*1;8K?^$}TA_+kn% zv>kyZ5IIzxB%^tA=|Elu03c{`VQ2KGenJm;F<7CKDJ2_|g^1>sue0D3n=>L=2w5Me zAdS>;mp2%GGp5;mcKR{2-mab|;d4Ds^{Cm|d$|_$O?WkJQ?zCQCb|U=6gXfG1_nsY z`l=QQpkj`u3vU54*e4$G!SOk7xpcw9)B;x}bpDdJn{sCdxe}gGuZ2u2`g==)^GtXZ zK7W8Eg0r+$T9Le+qcqsJ8vIs4K!qC|Vw*j(YmkX8Es+nLq+m+EY$o%sUkySI>7VU! z6Qqa2v}8_{-?XV9syGM~@_I|PNtR7iA&=HKR^KJwGDmr-Q8&Mi@BM@uZ9(vLWk|Jr z_->oN4gx=& zwR<2J&OLJ>eS{x{Nl0Q~XDT59WtGIdquirZPO?C)bZf^JDU<7*^t@@0*2Q7r{Gpz? z{(6!gAlsskRf}V=!=B7pR7A~0B;_D!mkr!<;ZC%rkHVm~NPz%hnUF%!Z3WfndYVfJOx1qi@`*p*4j z8x=bmY!;>T=7QwNCO6jH=j| zi0mX%Y)ElbOL@=KX`pvYB&;)H3YS9E7ushb&Di$Yo{SOzdZp-`aZ4eI?jK}HbUd47 zAJS&rN=s48Jd}mfxPo&HJ&^McfoShLHcgdNCo9D&$h6P$C{36xr-lgZV_Q|dD%Q;& zj_PR9HT=T}8E~4{3HMmk6Xqr3aH4Ia^LeYfH9(QA1v4PsmLjvs)^qHbP*cR`q8)ur zvXAJ0uMmAw^nWP_18Fncb)4XkF36?#+G>LUJr8%P=vRxIZ5JM<9LmXN9aI?W_`Qd| zn@qlUn^$l6TAjOkcG`k5@fX<&Qaf*c^yvX`3fSs(T2X3YbD9(Tdl9mPAJI3|%=5Uq z4zaqpk+iz()az(rMp+?WmSh3%)Xd{ToAdyA*5Fy^!HAz2)t92WXZ6=;*eW<>tc&g@ zqGZ1%12ccKj96_BjVy93P>h3Txu#sHow@T#C*o6A0{^hXw}(Kjgi_wFw)60vOo=^$ z?d@oV?^~0#SpXNt-|@0$Qwca-FvOQ0FpovPLe7=k7OQL}q@n8+mN!b%-;dqDXUvc+ zZ>)*UjZ;}8UX;>1*WPv^-V0xg;sa{4C<_thR26r6Y*=CR#`NI2uoVY`8({pWk*9TG zP=2;MW1D88T1y|dFW*Cnw?1ic5v&6BELze6+MKE9dJ7Rrmi+JEUn4wOTqWAr(yYMN zlZZnD+mB&DZi?(|*&y@H|4hvo47Tfiaq8}Ry-FoxyFe#mkf4*{Mb0~0U8#7O6`CUV zFr(%c(;+jm8j5YzmQ{rEpDxm)Y;Bw=w9bQfsPHH3;eP?lbJZv#0Oe?i$9=K}TPxhq zKB@MU#u{*q;ikj&Hd8_TU9J;{5GK3-YD|L{LuUbF3Og9m#onsd<=J1Z4gEewELz== z8@8(l98!0LMDgr0L`3J4y#u@6i41#pj5#6^rfHq7y3U!cQd!0Bih<7g{wQqqH`*Rv z(?vmzFb!rcZ$k|Hj>>M}?3b1t`7bpN)-Y8sC9a;MQ>p)~kk?!Da(!zH{F^a8e3j1+>*u&8$eZ7!DJ6kc?&US z`7ljDMGAmnW~lbC^+!;9zm!*fnJULd>t7R=ww@jrmFB>h)fc%ED%5C7G;fnZeQVh} z?JTeq6cvj80BNVNP>(}S(u%AImlA-DL)sq#QD{rKED0IA^^uIJA`fU^iAr&=RjT5a zp8j*Kavb?w!ho)kN#+gGf`~D|?^f}^Re64d=;B^is1b)d2}f*~YER}1))tVSXbl&N z*yBXN3=w9k;_fx-0r;d@70#fe)x_m2yxoaS4mD#k7?@&P$3fDwu;MkDq=@MlD8*^% zsh_v@XFurMU}deidq&wTn7 zDWpk_lC~I~6AHWD+{}Zxg65)~5rva}qaL}-LgkQ!jWS?OcFtns*h{-kea%T_`i?Z0 z9`7L=sirnL*nFw#8x9uA)y+2v94AQta_k}O+&d}jcpOy+O9SP;78P@Qlxyj(WLmXC zl%H^7kzx^=oFj8Uny-I~?wWxF#4#X_p~GK?286nJUeLNAibB3VA*-`BHu#JM``}VW z?0z5=HPsi3Fy^PJde;A42smrHrY=YuZsX4p4!5pv3(5+t?qp=6g#~6WhWF0P&T8(X z8~WkxvPQYlRu9!5-4-EQA2{W~t_jFA0W+R!OQLHrFJjxTq)>RBe9MsGnnSz2egAH> za!fS0w9`tla$Hm8r#P*M_rWmBSnU!AacK^crLZ0;toicM1oDV{$P2b(M_7enrDIFe z@aNchQr0^TXL(uy@PwTo8FW6&(4o%lVCE=MHoOxl_YJjQ1UM&grQCl{ zbSF(Oi~oJ%3qM8uq;j@@I8F0UilRXN%z>}J^6AyVpDDW3T+W2UBQTIO(KGY>uI6_S z?xP!Y=T<*oPe6qzQiqBoj`2rEKdFD`se_xaBL1!arzuYbQtHUxMVZ9@T~7wsJj!2= zay=CIf4{1qE0;0c`CIVs7p-Bi#NJW;Th^AXuS0pM*gs_r(BAd?7luX@X4d~h_(w>) zueGi)WZgc;))IH+m3{<>eyPv!N1d1Z`TsOs(t^wSvE4uLl&6J zaue77f(ormaT9Z3ROb0s(pv9!!3~SGT#?xv@99x^ZB7fLGhRRd=M%X_{a}9p%39j! zJ#sINZEVwH)_Fq3I{CTf>46qaE;Uk>k8%r$9_vI)k*qhpNfdu(lRo4uAF)&{Ex9M3@jV~9t#^2mx>36lZ#tIost?slEzd@1J5kj^>0-l+`opl zdZu=a`V2qE*)RUMZe>jm+>DfHHHv@M{7cg64|^$_h_Oc6i3cHm^+T zFIl(V!~-ocI=SEZ;aDQC%msC(ZhOS<4;0e3%eouvh)_q(FsApd21QoG>i{XPiH0_dc zllx@ladAN9MtYE|&XC?ZTw=Xj2o=}7_Uv9aCr?Tkk!CaoV;N7LxT6b7=RpJ#Xr;_S zdYX}X2Op)KHjNAj{Nh$ovx<@hDyR0+GpIc)>2_PB+R2NEy(~sj z$ughM_4qnYY`F*v4lH4)2zTC5qM%JcXt8EHzWH6hG0Yh4Wy@HPwc+d7uc6Yk?utO))ED7BWb%e5*$L)qJG@(;w(4Mb>?1dVa*8YRr<|}q@XQj6$E+*iQ+=XXw zgn-TvTrfK0KrZ9h5GGf043PNsDUOp>o51n4Z>t4&Px(&x4BA9f%)8S>(Qt35u!+qI z_$VrQ}N659WvC{u1Rn&R}F%222X?C7)Sd~K2&q;?Ik?L2mN6AZl6=Dhx|c?9=Q%gG)tih>LXqW$SnhJ^q&N~|Ph)55XgGp-~d4M$F@ zdop-SdpE{LvHBMcb36$&AwERS62u3H$66Cnw9fF_xd!J-^tv)*f+eF*qXKMOH|wY*L=3GtMpzHuwY#Is0NV zXP5h!$BpQq+-Ccsax0eDhDw)ctkwfO!Fi3&Io%82*S8`gxbMAwQ!8A&S0E*{>zY)V zZ5l>N{oPf*ixi9rlM=RwFn$EtK>?!RS0r)OFanYw@&y&!tFxTE1k7rH3S69`19jkb z0^blY(pA!rUr)9iX+Wb*M#r5UiCHf9!5^q(z%S6|%hc(<|EPXcA@F%{8B6xNb zJ(jwu;G1Q$FLi<6!7ooU3f4|JE_G@6gO5J*bx^KE&>2wa2=2PL673>_x!%qCFW(U^ zAB7RGE)R!{-q!t{yKB~(3vSC9x4%A7!>8ldpPBqEGmKPr{*L!=Sa7O@qm~|(JSu11 z>kSXmIB2VVe;cQZ4pi^@Ryt&7badEn2N4uzA6lFmp05bfxx1<7bz>jrWYliuanEnc zlZH6)COnF;({h!`@v*sUuJEz3ZH6r?d>!h%b(>ds<(&MP5YWBYN*gyDU!@?;$hb9T zxY;^2rf%fC-tv>ZPU7gG&Q0-{wt^i#54EjIg++CM$_iwvQ*H`O(Z^(CLgPmJQ9wm1 z6pA)gXz%UzMBNSP75`UA*-+xqE9^5$$a3{C3!;+ZY~%4bP-iRX zq93`NbwyOc54rD)sKoBnYxY$Wp_I=yX%92iN7!uJ#hoo>T1rMD5W!m`a50qLgQK?^ zb<;?w%~{N|){>gJ9emc}ZVtJA)~s$$NSd3hqcW@pN1QzUV?r5~ADlEsLlA7-zEdZ| z0xT&zu{al_pcp{8rbtAStP-i%cy>hkix~gb;4eV(>P|~ErvcF$tu{<P| z)Py4ZHG3xh)LP?Dt0-^`cWa*rIO4utdoIj<7k8>^jVu{Ow}Y#e1?SIl+}UkJN!n5DDh7!+7&xS(!?i+)&iGY| zT%xD4jP1Mt$m zK9P_jg*p(Teb(4>LK5-KVZ7xsf%(v=VOswq@7E=UQz}KVfdMD*Th$)o%3bfx0vC6w zduaUlV{ko&)D`4AH9}>bbZkXEp%kS2Pioo6A;(3QEI@e|ihP}i_6nk80tjZE*yrPt4pnZwsnATi(5B_5y&2PnVbWp2qqB0S^Z zbpK(eflI$n(Aci~BM`WIEv?EGvj(nI!$Hr`7r3`nnrLiIaJuRlFCVoGvinM<TWD+h!R*AF93pTf(54q9M)sT4IyX|8i{ z)m!`;KK)7*h3vJZrcOTFCA{j!v+GEex02M$@dkX}p&*<{Q=L9h+>ToJH| zKH6W}YD2jjQ#@vO33MgwIX=wYRnSWkBFcdmV-VGkFjjZ}nKGgQE?9J?8MG z0O0Ov5`z%lJls~46J+W#=b*ELD9ZfL1i3@|9*y`Km}oCX9XIl(2ZCOP*_|4789$P!T;9Kh`ZpSYPl~cS zL1ix0*>IIEKpM(7D`vU(6#*^Z4qRh!w3OB>0{U}VI1z5RTg4q`jl#_F*D`;vG049d zU(A$4?ikWEw?;*+I<5&2ktc27N$&};GgnI7x%uS<&kwgAx~o-B_U2eflOUofOoG(# zh@l}TRk)@IaI@0Ero7Cmb`~0Oc<@Kv@MSTC2ail~nu7&cQg}$bxNl@+4EGQJPf^z% z&33khJ50jGB{e~)5ur%hMibJaT2r*BM?$KtN2+O3qS_j-K4{3am{GJ5(RR2o5h_X_ z8e=?iJEKBPGXyDCDa8bps49vIiQeBW?)~qqv(Dc8oW0K3-&uR@@0|VF>9*l@u{qpM z)a&n#L^|vZ2o<=@fab@&#eUAvtC$x;+7lL`AS_hR>YL{6``z49= z3KJQB%gd9cCpz3UxxVhH8NRrWE>(IXZq``Gy?dgH{el#!!5>P$X{Ea!xp;xFf#g$< zBi{&~(L=arwC2{achT_=R=%6%SWF^hQ!L=a6MBwyH*@0x(>qLUQ5w_qy*G8lHt)n| z>KnBEI+NzK0fo7JL*Bzkx;t=C>^2n>IS99CRe*u0{q_INyLyie+FQYIemShnDzbL7 zrAEBxsJKa( zn;IWH&DFzv6etJxQvTN^%oQf_lw*?G_iG#TeN2+a&yi13H8%~i-^spBgDH+AZVOb| zuRAYPj^uDuX9|0y-xE}KruLR3D%QRI&MvEM#AbWak~l8n0D*n>4Lo#v%-GihPK#nO z>E3Pefdm5a;M|I85HDY~a_k;DNOgfb^a$M~&vtmiPvMf6ils~Vzrp_X|Fk3g3y>y< ze8skzT*#eP`zY&*rHk`#suoVLI1_)QRwSif>`*72+C@Kym#+-o!1*)t=)e}7AAS!V zMGw*XBE1#{&P6H@U3(`yQLS4sFt>22SgJG$nxbU|3A1m?435MWwrR=og(==wDu!$b zVNO^bC)TTd1@+k9v^*709x^TS4*1XMye%!eY_zR!t!S~S>>}zhC2adI{PeBzdSdpa zftob?{r&f_buFCy%<{z}*^dg1G9JMED@z_?~Oadv()B_r~Ept^6ByVz|Ou zW`ImHReGwwvG7^e{0KkETsZoPI?8;e99TK8&I1avEJEggS|tNN;K75qr4~RV;Llaa z9`5e_j*CiwN;?8%2>wgQ-2&3o(A3iW`U@Kus1AZ`1xkZ7ED?sM4+dVd!Vt;vyyhQR z0!@!Nsl)Y>*^Mj#P!Oc{?PegFtmufw$CZp;jxY+1PoeE`Z%mTh`VBAY9Xu+_O5xXb zJsBZY^v8bBPb({mo_hXZEn|gP1O(}Nexkt5$SAb8_K{=ESn9qz6(iTvUInYc5EsB`1neB!rRYV+&|*D_eY2lQH*T*?>Y(pj1mN zItzL$*oSHr#q(z;-G2S90v*=Cc@9vg8~FU9>9eGsmu#`xC8jUA=t>{HOAH%blm0vX zVXX5$JqATeO`nuwbCb+r%jO}YqjKkc%xg?4Tobm|0@r*!ssfw#hd>j$cy~eU24>}J z{kOhrmOS??G22$p5e(8<`sp<3#6XxQ7{-g34e~T^M6$Qiln#uA9BVz%Y;+Ik#eGuk zF%T%okAmwUUdLtYltTCjzXV98t~L=j{Su)a=vAE6V@1)M+wn z3ab!7b9HU+xE6#%N;A+|9-n-|3od{JI?fon?Ok04m@%;E#jq-Zav5K-^jk2OP46}y z)(-B^Kt7tTMbGR)hV7=J5TcRh{5J2=$maH>?P`!6DA`b*X`6B(92$$bI<;C}GaGj= zV+YSp@eiD@Pb11O_RLd_pUXSkte9-skACMppn)YBNSQVkGJb+-vb7tt9DXGJ)8qQASHD&XkN$pD7sm;N#UdW5jy4G zjy9)RFFt_D2`=V;(XXBM>NGwtz7rqR^&?rd!=*==NgG>+bsi#`!1IvEYQ*34;#2g@ z$>}i_&3iKVRN3MR>w)^}+u= hilu>h&3b9?b19HNjT6VG3`7msT Date: Fri, 6 Mar 2020 07:45:12 -0500 Subject: [PATCH 34/36] fix click handlers on data tables (#5087) --- src/panels/config/zha/zha-devices-data-table.ts | 6 +++--- src/panels/config/zha/zha-groups-data-table.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/panels/config/zha/zha-devices-data-table.ts b/src/panels/config/zha/zha-devices-data-table.ts index 619d81bb90..52ee1a744f 100644 --- a/src/panels/config/zha/zha-devices-data-table.ts +++ b/src/panels/config/zha/zha-devices-data-table.ts @@ -101,9 +101,9 @@ export class ZHADevicesDataTable extends LitElement { } private async _handleClicked(ev: CustomEvent) { - const ieee = (ev.target as HTMLElement) - .closest("tr")! - .getAttribute("data-row-id")!; + const ieee = ((ev.target as HTMLElement).closest( + ".mdc-data-table__row" + ) as any).rowId; showZHADeviceInfoDialog(this, { ieee }); } } diff --git a/src/panels/config/zha/zha-groups-data-table.ts b/src/panels/config/zha/zha-groups-data-table.ts index a47cca603c..20bfc00b36 100644 --- a/src/panels/config/zha/zha-groups-data-table.ts +++ b/src/panels/config/zha/zha-groups-data-table.ts @@ -110,9 +110,9 @@ export class ZHAGroupsDataTable extends LitElement { } private _handleRowClicked(ev: CustomEvent) { - const groupId = (ev.target as HTMLElement) - .closest("tr")! - .getAttribute("data-row-id")!; + const groupId = ((ev.target as HTMLElement).closest( + ".mdc-data-table__row" + ) as any).rowId; navigate(this, `/config/zha/group/${groupId}`); } } From 1cb614c8a8f7339280577823a3dd6785552c7e36 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 6 Mar 2020 13:46:11 +0100 Subject: [PATCH 35/36] Forgot border height (#5088) --- src/components/data-table/ha-data-table.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/data-table/ha-data-table.ts b/src/components/data-table/ha-data-table.ts index a3ea1ff84a..ff43fa59dc 100644 --- a/src/components/data-table/ha-data-table.ts +++ b/src/components/data-table/ha-data-table.ts @@ -189,7 +189,7 @@ export class HaDataTable extends LitElement { })}" style=${styleMap({ height: this.autoHeight - ? `${this._filteredData.length * 52 + 56}px` + ? `${this._filteredData.length * 53 + 57}px` : `calc(100% - ${this._header?.clientHeight}px)`, })} > From a0900afba3c47fa376df6fb168d2502fffa98eb6 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 6 Mar 2020 14:00:36 +0100 Subject: [PATCH 36/36] Bumped version to 20200306.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b37397edb7..56d9897b2a 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20200228.0", + version="20200306.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/home-assistant-polymer", author="The Home Assistant Authors",