From d174571a6a238392f57e9b4d258d7724e6013a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Tue, 6 Oct 2026 16:46:09 -0400 Subject: [PATCH 01/14] Let capable hardware run speech recognition and synthesis on the device The installer only ever left speech on the public Open Voice OS servers, which are slow when the community is busy and send every recording over the internet. A Raspberry Pi 5 with 8 GB, or anything at least as capable, can run onnx-asr and phoonnx itself. The TUI now asks where speech runs, after the feature checklist, but only on alpha virtualenv installs whose profile has audio and whose hardware passes: a 64-bit CPU with AVX2 or NEON, at least 7.5 GiB of memory, and no Raspberry Pi older than the 5 family. Everything else stays on the public servers, and the summary says why. Scenario files get a speech_engine option. The plugins and models are the ones the installed ovos-config recommends for the locale, read from its offline recommendations rather than copied here; only their stt and tts sections are merged, because autoconfigure also rewrites units, wake words and intents this installer manages. The public STT server stays as the fallback for an utterance local recognition fails on, the models are fetched during the install, and uninstall removes them. Telemetry reports stt_engine, tts_engine and local_speech_capable. Co-Authored-By: Claude Opus 5.5 --- README.md | 4 + ansible/roles/ovos_config/defaults/main.yml | 9 + .../ovos_config/files/speech_prefetch.py | 28 ++ .../files/speech_recommendation.py | 49 ++++ ansible/roles/ovos_config/tasks/install.yml | 26 +- ansible/roles/ovos_config/tasks/speech.yml | 128 +++++++++ .../ovos_config/templates/mycroft.conf.j2 | 7 + .../roles/ovos_installer/defaults/main.yml | 5 + ansible/roles/ovos_installer/tasks/assert.yml | 17 ++ ansible/roles/ovos_services/defaults/main.yml | 3 + .../roles/ovos_telemetry/defaults/main.yml | 5 + ansible/roles/ovos_telemetry/tasks/main.yml | 3 + ansible/roles/ovos_virtualenv/tasks/venv.yml | 12 + .../virtualenv/core-requirements.txt.j2 | 8 + .../virtualenv/satellite-requirements.txt.j2 | 8 + docs/automation.md | 1 + docs/telemetry.md | 3 + scripts/sync_translations.py | 20 +- setup.sh | 7 + tests/bats/code_quality.bats | 52 +++- tests/bats/locales.bats | 66 +++++ tests/bats/speech.bats | 270 ++++++++++++++++++ tests/bats/tui_navigation.bats | 74 +++++ translations/ca-es/strings.json | 13 +- translations/da/strings.json | 13 +- translations/de-de/strings.json | 13 +- translations/en-us/strings.json | 15 +- translations/es-es/strings.json | 13 +- translations/eu-es/strings.json | 13 +- translations/fr-fr/strings.json | 13 +- translations/gl-es/strings.json | 13 +- translations/hi-in/strings.json | 13 +- translations/it-it/strings.json | 13 +- translations/kab-dz/strings.json | 15 +- translations/nl-nl/strings.json | 13 +- translations/pl-pl/strings.json | 13 +- translations/pt-pt/strings.json | 13 +- tui/locales/ca-es/speech.sh | 16 ++ tui/locales/ca-es/summary.sh | 8 +- tui/locales/da/speech.sh | 16 ++ tui/locales/da/summary.sh | 8 +- tui/locales/de-de/speech.sh | 16 ++ tui/locales/de-de/summary.sh | 8 +- tui/locales/en-us/speech.sh | 16 ++ tui/locales/en-us/summary.sh | 8 +- tui/locales/es-es/speech.sh | 16 ++ tui/locales/es-es/summary.sh | 8 +- tui/locales/eu-es/speech.sh | 16 ++ tui/locales/eu-es/summary.sh | 8 +- tui/locales/fr-fr/speech.sh | 16 ++ tui/locales/fr-fr/summary.sh | 8 +- tui/locales/gl-es/speech.sh | 16 ++ tui/locales/gl-es/summary.sh | 8 +- tui/locales/hi-in/speech.sh | 16 ++ tui/locales/hi-in/summary.sh | 8 +- tui/locales/it-it/speech.sh | 16 ++ tui/locales/it-it/summary.sh | 8 +- tui/locales/kab-dz/speech.sh | 16 ++ tui/locales/kab-dz/summary.sh | 8 +- tui/locales/nl-nl/speech.sh | 16 ++ tui/locales/nl-nl/summary.sh | 8 +- tui/locales/pl-pl/speech.sh | 16 ++ tui/locales/pl-pl/summary.sh | 8 +- tui/locales/pt-pt/speech.sh | 16 ++ tui/locales/pt-pt/summary.sh | 8 +- tui/main.sh | 4 + tui/navigation.sh | 6 + tui/speech.sh | 62 ++++ tui/summary.sh | 20 ++ utils/constants.sh | 5 +- utils/scenario.sh | 11 + utils/speech.sh | 96 +++++++ 72 files changed, 1493 insertions(+), 38 deletions(-) create mode 100644 ansible/roles/ovos_config/files/speech_prefetch.py create mode 100644 ansible/roles/ovos_config/files/speech_recommendation.py create mode 100644 ansible/roles/ovos_config/tasks/speech.yml create mode 100644 tests/bats/speech.bats create mode 100644 tui/locales/ca-es/speech.sh create mode 100644 tui/locales/da/speech.sh create mode 100644 tui/locales/de-de/speech.sh create mode 100644 tui/locales/en-us/speech.sh create mode 100644 tui/locales/es-es/speech.sh create mode 100644 tui/locales/eu-es/speech.sh create mode 100644 tui/locales/fr-fr/speech.sh create mode 100644 tui/locales/gl-es/speech.sh create mode 100644 tui/locales/hi-in/speech.sh create mode 100644 tui/locales/it-it/speech.sh create mode 100644 tui/locales/kab-dz/speech.sh create mode 100644 tui/locales/nl-nl/speech.sh create mode 100644 tui/locales/pl-pl/speech.sh create mode 100644 tui/locales/pt-pt/speech.sh create mode 100644 tui/speech.sh create mode 100644 utils/speech.sh diff --git a/README.md b/README.md index 1999c753..a662d33b 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,10 @@ speech, skills, services, the lot. - **Give it a brain.** An optional LLM fallback answers what the skills do not, pointed at whichever OpenAI-compatible endpoint you like — including one you host yourself. +- **Keep your voice at home.** On a Raspberry Pi 5 with 8 GB, or anything at + least as capable, speech recognition and the assistant's voice can run on the + device itself instead of on the community's public servers. Alpha channel + only, for now. - **Put it in every room.** HiveMind satellites share a single assistant across several devices, so the Pi in the hallway and the one in the kitchen are the same assistant. diff --git a/ansible/roles/ovos_config/defaults/main.yml b/ansible/roles/ovos_config/defaults/main.yml index 9d4a36a8..a8f3e06d 100644 --- a/ansible/roles/ovos_config/defaults/main.yml +++ b/ansible/roles/ovos_config/defaults/main.yml @@ -83,6 +83,15 @@ ovos_config_homescreen_probe: >- from importlib.metadata import entry_points; print(next((e.name for g in ('opm.skill', 'ovos.plugin.skill') for e in entry_points(group=g) if 'homescreen' in e.name), '')) +# Local speech (ovos_installer_speech_engine: local) - see tasks/speech.yml. +# The public STT server stays configured as the fallback for an utterance the +# local plugin fails on or hears nothing in. +ovos_config_speech_stt_fallback: ovos-stt-plugin-server +ovos_config_speech_prefetch_retries: "{{ ovos_installer_speech_prefetch_retries | default(3) }}" +ovos_config_speech_prefetch_delay: "{{ ovos_installer_speech_prefetch_delay | default(10) }}" +# The stt and tts sections the last run wrote, carried through the template so a +# re-run does not drop them only for speech.yml to put them back. +ovos_config_speech_current: {} ovos_config_mycroft_conf_owner: >- {{ ovos_installer_user diff --git a/ansible/roles/ovos_config/files/speech_prefetch.py b/ansible/roles/ovos_config/files/speech_prefetch.py new file mode 100644 index 00000000..e3f4a6ff --- /dev/null +++ b/ansible/roles/ovos_config/files/speech_prefetch.py @@ -0,0 +1,28 @@ +"""Load the configured local STT and TTS plugins once. + +Usage: speech_prefetch.py + +Both plugins fetch their model when they are created, so doing it here moves +a download of up to a few gigabytes from the first utterance to the install. +""" +import base64 +import json +import sys + +from ovos_plugin_manager.stt import load_stt_plugin +from ovos_plugin_manager.tts import load_tts_plugin + +sections, lang = json.loads(base64.b64decode(sys.argv[1])), sys.argv[2] + +for kind, load in (("stt", load_stt_plugin), ("tts", load_tts_plugin)): + section = sections.get(kind) + if not section: + continue + module = section["module"] + plugin = load(module) + if plugin is None: + sys.exit(f"{kind} plugin {module} is not installed") + config = dict(section.get(module) or {}) + config.setdefault("lang", lang) + plugin(config=config) + print(f"{kind}: {module} ready") diff --git a/ansible/roles/ovos_config/files/speech_recommendation.py b/ansible/roles/ovos_config/files/speech_recommendation.py new file mode 100644 index 00000000..7be0bae1 --- /dev/null +++ b/ansible/roles/ovos_config/files/speech_recommendation.py @@ -0,0 +1,49 @@ +"""Print the stt and tts sections ovos-config recommends for on-device speech. + +Usage: speech_recommendation.py + +Reads the offline recommendations shipped with the ovos-config installed in +the OVOS virtualenv - the files `ovos-config autoconfigure --offline` merges - +so the plugins and models follow that release rather than a copy kept here. +Prints {"stt": {...} or null, "tts": {...} or null}; null means the release +has no recommendation for the locale. +""" +import json +import os +import sys + +import ovos_config +from ovos_config.config import LocalConf + +lang, gender = sys.argv[1], sys.argv[2] +root = os.path.join(os.path.dirname(ovos_config.__file__), "recommends") + + +def find(folder): + path = os.path.join(root, folder) + if not os.path.isdir(path): + return None + try: + from ovos_config.utils import find_recommends_file + except ImportError: + # ovos-config 2.x: the exact tag, then a file sharing its primary subtag. + exact = os.path.join(path, f"{lang.lower()}.conf") + if os.path.isfile(exact): + return exact + primary = lang.lower().split("-")[0] + for name in sorted(os.listdir(path)): + if name.endswith(".conf") and name.lower().startswith(primary): + return os.path.join(path, name) + return None + return find_recommends_file(path, lang) + + +def section(folder, key): + path = find(folder) + return LocalConf(path).get(key) if path else None + + +print(json.dumps({ + "stt": section("offline_stt", "stt"), + "tts": section(f"offline_{gender}", "tts"), +})) diff --git a/ansible/roles/ovos_config/tasks/install.yml b/ansible/roles/ovos_config/tasks/install.yml index 0d972493..f8f298c7 100644 --- a/ansible/roles/ovos_config/tasks/install.yml +++ b/ansible/roles/ovos_config/tasks/install.yml @@ -71,10 +71,11 @@ ovos_config_spoken_time_format: "half" when: ovos_config_geoip_country | default('') in ovos_config_geoip_half_time_countries -# Read the configured homescreen back before the template overwrites the file, so -# a re-run keeps the id the last run resolved instead of dropping it and waiting -# for the post-install step to put it back (which would make every run changed). -- name: Read the configured homescreen skill id +# Read the configured homescreen and local speech back before the template +# overwrites the file, so a re-run keeps what the last run resolved instead of +# dropping it and waiting for the post-install steps to put it back (which would +# make every run changed). +- name: Read the configuration the last run wrote ansible.builtin.slurp: src: "{{ ovos_config_mycroft_conf_path }}" register: ovos_config_existing_conf @@ -103,6 +104,23 @@ ovos_config_idle_display_skill: "" changed_when: false +- name: Keep the local speech configuration this host already resolved + when: + - ovos_config_existing_conf.content is defined + - (ovos_installer_speech_engine | default('public')) == 'local' + block: + - name: Read the speech sections out of the current configuration + vars: + _existing: "{{ ovos_config_existing_conf.content | b64decode | from_json }}" + ansible.builtin.set_fact: + ovos_config_speech_current: "{{ _existing | dict2items | selectattr('key', 'in', ['stt', 'tts']) | items2dict }}" + changed_when: false + rescue: + - name: Start over when the current configuration cannot be read + ansible.builtin.set_fact: + ovos_config_speech_current: {} + changed_when: false + - name: Generate mycroft.conf ansible.builtin.template: src: mycroft.conf.j2 diff --git a/ansible/roles/ovos_config/tasks/speech.yml b/ansible/roles/ovos_config/tasks/speech.yml new file mode 100644 index 00000000..8921d7c4 --- /dev/null +++ b/ansible/roles/ovos_config/tasks/speech.yml @@ -0,0 +1,128 @@ +--- +# Local speech: STT and TTS run on this host with the plugins and models the +# installed ovos-config recommends for the locale. Those are the files +# `ovos-config autoconfigure --offline` reads, but only their stt and tts +# sections are taken: autoconfigure also merges per-language defaults for units, +# wake words and intents, which this installer sets itself. +# +# The caller passes ovos_config_speech_python, the OVOS virtualenv's Python. +- name: Resolve the speech plugins ovos-config recommends for this locale + become: true + become_user: "{{ ovos_installer_user }}" + ansible.builtin.command: + argv: + - "{{ ovos_config_speech_python }}" + - -c + - "{{ lookup('ansible.builtin.file', 'speech_recommendation.py') }}" + - "{{ ovos_installer_locale }}" + - "{{ 'female' if ovos_installer_ovos_config_tts_gender == '--female' else 'male' }}" + register: ovos_config_speech_probe + changed_when: false + +# A failed local recognition, or one that hears nothing, goes to the public +# server for that utterance instead of leaving the user unanswered. +- name: Keep the recommended sections + vars: + _recommended: "{{ ovos_config_speech_probe.stdout | from_json }}" + ansible.builtin.set_fact: + ovos_config_speech_wanted: >- + {{ + ({'stt': _recommended.stt | combine({'fallback_module': ovos_config_speech_stt_fallback})} + if _recommended.stt else {}) + | combine({'tts': _recommended.tts} if _recommended.tts else {}) + }} + +# ovos-config does not recommend on-device speech for every language, and which +# ones it covers changes from one release to the next. +- name: Report what stays on the public servers + ansible.builtin.debug: + msg: >- + The ovos-config release in this virtualenv recommends no on-device + {{ item | upper }} for {{ ovos_installer_locale }}, so {{ item | upper }} + stays on the public servers. + loop: "{{ ['stt', 'tts'] | difference(ovos_config_speech_wanted.keys() | list) }}" + +- name: Read mycroft.conf for the speech configuration + ansible.builtin.slurp: + src: "{{ ovos_config_mycroft_conf_path }}" + register: ovos_config_speech_conf + failed_when: false + changed_when: false + when: ovos_config_speech_wanted | length > 0 + +# Same reasoning as homescreen.yml: a hand-edited mycroft.conf that is not +# plain JSON is not a reason to end the install. +- name: Parse mycroft.conf + when: ovos_config_speech_conf.content is defined + block: + - name: Read the current configuration + ansible.builtin.set_fact: + ovos_config_speech_conf_current: "{{ ovos_config_speech_conf.content | b64decode | from_json }}" + ovos_config_speech_conf_readable: true + changed_when: false + rescue: + - name: Note that mycroft.conf could not be read as JSON + ansible.builtin.set_fact: + ovos_config_speech_conf_readable: false + changed_when: false + + - name: Report the speech configuration that has to be set by hand + ansible.builtin.debug: + msg: >- + {{ ovos_config_mycroft_conf_path }} is not plain JSON, so local speech + was not configured and the public servers stay in use. To switch, add + these sections to it by hand: {{ ovos_config_speech_wanted | to_json }} + +- name: Write the local speech configuration + vars: + _conf: "{{ ovos_config_speech_conf_current | default({}, true) }}" + ansible.builtin.copy: + content: "{{ _conf | combine(ovos_config_speech_wanted) | to_nice_json }}\n" + dest: "{{ ovos_config_mycroft_conf_path }}" + owner: "{{ ovos_config_mycroft_conf_owner }}" + group: "{{ ovos_config_mycroft_conf_group }}" + mode: "{{ ovos_config_mycroft_conf_mode }}" + when: + - ovos_config_speech_conf_readable | default(false) | bool + - _conf is mapping + - (_conf | combine(ovos_config_speech_wanted)) != _conf + +- name: Record where speech runs + vars: + _applied: "{{ ovos_config_speech_conf_readable | default(false) | bool }}" + ansible.builtin.set_fact: + ovos_config_speech_stt_engine: "{{ 'local' if _applied and 'stt' in ovos_config_speech_wanted else 'public' }}" + ovos_config_speech_tts_engine: "{{ 'local' if _applied and 'tts' in ovos_config_speech_wanted else 'public' }}" + +# Both plugins fetch their model when they are created. Doing that here turns a +# download of up to a few gigabytes into part of the install, instead of a +# listener that sits silent through its first start. +- name: Download the local speech models + when: + - ovos_config_speech_conf_readable | default(false) | bool + - ovos_config_speech_wanted | length > 0 + block: + - name: Load the local speech plugins once so they fetch their models + become: true + become_user: "{{ ovos_installer_user }}" + ansible.builtin.command: + argv: + - "{{ ovos_config_speech_python }}" + - -c + - "{{ lookup('ansible.builtin.file', 'speech_prefetch.py') }}" + # Encoded because Ansible turns a templated string that looks like a + # dict back into one, and the script would get its Python repr. + - "{{ ovos_config_speech_wanted | to_json | b64encode }}" + - "{{ ovos_installer_locale }}" + register: ovos_config_speech_prefetch + changed_when: false + until: ovos_config_speech_prefetch is success + retries: "{{ ovos_config_speech_prefetch_retries | int }}" + delay: "{{ ovos_config_speech_prefetch_delay | int }}" + rescue: + - name: Report the models that could not be downloaded + ansible.builtin.debug: + msg: >- + The local speech models could not be downloaded + ({{ (ovos_config_speech_prefetch.stderr_lines | default(['unknown error'], true)) | last }}). + The speech services download them when they start instead. diff --git a/ansible/roles/ovos_config/templates/mycroft.conf.j2 b/ansible/roles/ovos_config/templates/mycroft.conf.j2 index b5cf47ae..5c98a602 100644 --- a/ansible/roles/ovos_config/templates/mycroft.conf.j2 +++ b/ansible/roles/ovos_config/templates/mycroft.conf.j2 @@ -146,6 +146,13 @@ } {% endif %} }, +{% endif %} +{# Local speech is written by tasks/speech.yml once the virtualenv can say what + ovos-config recommends; a re-run carries the last result through here. #} +{% if (ovos_installer_speech_engine | default('public')) == 'local' %} +{% for _speech_section, _speech_value in (ovos_config_speech_current | default({}, true)).items() %} + "{{ _speech_section }}": {{ _speech_value | to_json }}, +{% endfor %} {% endif %} "websocket": { "host": "{{ '0.0.0.0' if ovos_installer_method == "containers" else '127.0.0.1' }}", diff --git a/ansible/roles/ovos_installer/defaults/main.yml b/ansible/roles/ovos_installer/defaults/main.yml index 07da7a50..dbd38f00 100644 --- a/ansible/roles/ovos_installer/defaults/main.yml +++ b/ansible/roles/ovos_installer/defaults/main.yml @@ -155,6 +155,11 @@ ovos_installer_mark2_vad_pre_wake_enabled: false ovos_installer_listener_multiplier: 1.5 ovos_installer_tuning_source_volume: 1.0 ovos_installer_cpu_is_capable: false +# Where STT and TTS run: "public" (the Open Voice OS servers, the default) or +# "local" (on this host, see ovos_config/tasks/speech.yml). setup.sh only passes +# local for alpha virtualenv installs on hardware that can run it. +ovos_installer_speech_engine: public +ovos_installer_local_speech_capable: false ovos_installer_locale: "en-us" ovos_installer_feature_skills: true ovos_installer_feature_extra_skills: false diff --git a/ansible/roles/ovos_installer/tasks/assert.yml b/ansible/roles/ovos_installer/tasks/assert.yml index b3ce3878..2c5bd2a0 100644 --- a/ansible/roles/ovos_installer/tasks/assert.yml +++ b/ansible/roles/ovos_installer/tasks/assert.yml @@ -31,6 +31,23 @@ - not (ovos_installer_is_cleaning | bool) - ovos_installer_feature_gui | bool +- name: Assert the speech engine is one the installer knows + ansible.builtin.assert: + that: + - ovos_installer_speech_engine in ['local', 'public'] + fail_msg: "ovos_installer_speech_engine must be local or public." + +- name: Assert local speech constraints + ansible.builtin.assert: + that: + - ovos_installer_method == 'virtualenv' + - ovos_installer_channel == 'alpha' + - ovos_installer_profile != 'server' + fail_msg: "Local speech is only available on alpha virtualenv installs whose profile has audio." + when: + - not (ovos_installer_is_cleaning | bool) + - ovos_installer_speech_engine == 'local' + - name: Assert containers required variables ansible.builtin.assert: that: diff --git a/ansible/roles/ovos_services/defaults/main.yml b/ansible/roles/ovos_services/defaults/main.yml index 10745d48..885c8499 100644 --- a/ansible/roles/ovos_services/defaults/main.yml +++ b/ansible/roles/ovos_services/defaults/main.yml @@ -243,6 +243,7 @@ ovos_services_remove_directories: - "{{ ovos_installer_user_home }}/.cache/mycroft" - "{{ ovos_installer_user_home }}/.cache/ovos_gui" - "{{ ovos_installer_user_home }}/.cache/ovos-installer" + - "{{ ovos_installer_user_home }}/.cache/phoonnx" - "{{ ovos_installer_user_home }}/.config/mycroft" - "{{ ovos_installer_user_home }}/.config/hivemind" - "{{ ovos_installer_user_home }}/.config/OpenVoiceOS" @@ -254,6 +255,8 @@ ovos_services_remove_directories: - "{{ ovos_installer_user_home }}/.local/share/vosk" - "{{ ovos_installer_user_home }}/.local/share/mycroft" - "{{ ovos_installer_user_home }}/.local/share/OpenVoiceOS" + - "{{ ovos_installer_user_home }}/.local/share/ovos_stt_plugin_onnxasr" + - "{{ ovos_installer_user_home }}/.local/share/phoonnx" - "{{ ovos_installer_user_home }}/.local/share/precise-onnx" - "{{ ovos_installer_user_home }}/.local/share/precise-lite" - "{{ ovos_installer_user_home }}/.local/share/wallpapers" diff --git a/ansible/roles/ovos_telemetry/defaults/main.yml b/ansible/roles/ovos_telemetry/defaults/main.yml index 4cad0ba5..c23a3e1d 100644 --- a/ansible/roles/ovos_telemetry/defaults/main.yml +++ b/ansible/roles/ovos_telemetry/defaults/main.yml @@ -2,6 +2,11 @@ ovos_telemetry_feature_gui: "{{ ovos_installer_feature_gui | default(false) }}" ovos_telemetry_feature_homeassistant: "{{ ovos_installer_feature_homeassistant | default(false) }}" ovos_telemetry_feature_llm: "{{ ovos_installer_feature_llm | default(false) }}" +# Where speech ended up, not what was asked for: a locale the installed +# ovos-config recommends no on-device model for stays public when local was +# picked. ovos_config/tasks/speech.yml sets these; every other run is public. +ovos_telemetry_stt_engine: "{{ ovos_config_speech_stt_engine | default('public') }}" +ovos_telemetry_tts_engine: "{{ ovos_config_speech_tts_engine | default('public') }}" ovos_telemetry_geoip_url: http://ip-api.com/json ovos_telemetry_url: "https://telemetry.smartgic.io/ovos-installer/metrics/" diff --git a/ansible/roles/ovos_telemetry/tasks/main.yml b/ansible/roles/ovos_telemetry/tasks/main.yml index 8233f31b..cc265964 100644 --- a/ansible/roles/ovos_telemetry/tasks/main.yml +++ b/ansible/roles/ovos_telemetry/tasks/main.yml @@ -76,6 +76,9 @@ gui_feature: "{{ ovos_telemetry_feature_gui | default(false) | bool }}" llm_feature: "{{ ovos_telemetry_feature_llm | default(false) | bool }}" cpu_capable: "{{ ovos_installer_cpu_is_capable | default(false) | bool }}" + local_speech_capable: "{{ ovos_installer_local_speech_capable | default(false) | bool }}" + stt_engine: "{{ ovos_telemetry_stt_engine }}" + tts_engine: "{{ ovos_telemetry_tts_engine }}" tuning_enabled: "{{ (ovos_installer_tuning | default('no')) == 'yes' }}" country: "{{ _telemetry_country }}" hardware: >- diff --git a/ansible/roles/ovos_virtualenv/tasks/venv.yml b/ansible/roles/ovos_virtualenv/tasks/venv.yml index 8ab033c9..3cecf75b 100644 --- a/ansible/roles/ovos_virtualenv/tasks/venv.yml +++ b/ansible/roles/ovos_virtualenv/tasks/venv.yml @@ -523,3 +523,15 @@ changed_when: false when: - ovos_installer_ovos_config_autoconfigure_enabled | default(false) | bool + +# The plugins and models come from the ovos-config this virtualenv now holds, +# so local speech can only be configured once it is installed. +- name: Configure local speech + ansible.builtin.include_role: + name: ovos_config + tasks_from: speech.yml + vars: + ovos_config_speech_python: "{{ ovos_virtualenv_path }}/bin/python" + when: + - not (ovos_virtualenv_is_cleaning | bool) + - (ovos_installer_speech_engine | default('public')) == 'local' diff --git a/ansible/roles/ovos_virtualenv/templates/virtualenv/core-requirements.txt.j2 b/ansible/roles/ovos_virtualenv/templates/virtualenv/core-requirements.txt.j2 index f95e13c9..649a2fb3 100644 --- a/ansible/roles/ovos_virtualenv/templates/virtualenv/core-requirements.txt.j2 +++ b/ansible/roles/ovos_virtualenv/templates/virtualenv/core-requirements.txt.j2 @@ -54,6 +54,14 @@ ovos-phal-plugin-ipgeo ovos-tts-plugin-piper {% endif %} ovos-tts-plugin-polly +{% if (ovos_installer_speech_engine | default('public')) == 'local' %} +# Local speech: the STT and TTS plugins ovos-config recommends for running on +# this host; ovos_config/tasks/speech.yml picks their models. onnx-asr fetches +# models through huggingface_hub, which only its hub extra brings in. +ovos-stt-plugin-onnx-asr +onnx-asr[cpu,hub] +phoonnx +{% endif %} {% if ovos_installer_locale == "ca-es" %} ovos-tts-plugin-matxa-multispeaker-cat diff --git a/ansible/roles/ovos_virtualenv/templates/virtualenv/satellite-requirements.txt.j2 b/ansible/roles/ovos_virtualenv/templates/virtualenv/satellite-requirements.txt.j2 index afef28d3..78effe82 100644 --- a/ansible/roles/ovos_virtualenv/templates/virtualenv/satellite-requirements.txt.j2 +++ b/ansible/roles/ovos_virtualenv/templates/virtualenv/satellite-requirements.txt.j2 @@ -46,6 +46,14 @@ ovos-phal-plugin-ipgeo ovos-tts-plugin-piper {% endif %} ovos-tts-plugin-polly +{% if (ovos_installer_speech_engine | default('public')) == 'local' %} +# Local speech: the STT and TTS plugins ovos-config recommends for running on +# this host; ovos_config/tasks/speech.yml picks their models. onnx-asr fetches +# models through huggingface_hub, which only its hub extra brings in. +ovos-stt-plugin-onnx-asr +onnx-asr[cpu,hub] +phoonnx +{% endif %} {% if ovos_installer_locale == "ca-es" %} ovos-tts-plugin-matxa-multispeaker-cat diff --git a/docs/automation.md b/docs/automation.md index 4c44734c..7ea325e2 100644 --- a/docs/automation.md +++ b/docs/automation.md @@ -44,6 +44,7 @@ Then run the installer as usual. More examples live in | `llm.key` | API key for that endpoint, required with `features.llm` | | `llm.model` | Model name to use, required with `features.llm` | | `llm.persona` | System prompt for `ovos-persona`, required with `features.llm` | +| `speech_engine` | Where speech recognition and the voice run: `public` (the default) uses the Open Voice OS public servers, `local` runs them on the device. Local needs the `alpha` channel, the `virtualenv` method, a profile with audio and a Raspberry Pi 5 with 8 GB or an equivalent machine; anywhere else the installer says so and uses `public` | | `raspberry_pi_tuning` | Maximum-performance tuning for a Pi, including an overclocking prompt | | `share_telemetry` | Share anonymous usage statistics — see [Telemetry](telemetry.md) | | `share_usage_telemetry` | Share detailed usage data — see [Telemetry](telemetry.md) | diff --git a/docs/telemetry.md b/docs/telemetry.md index 552ee6eb..f6a6efdb 100644 --- a/docs/telemetry.md +++ b/docs/telemetry.md @@ -26,6 +26,7 @@ The table below lists the collected data. See the [Ansible task](https://github. | `container` | OVOS installed into containers | | `country` | Country where OVOS was installed | | `cpu_capable` | Whether the CPU supports AVX2 or SIMD instructions | +| `local_speech_capable` | Whether the hardware could run speech on the device | | `display_server` | Whether X or Wayland is used as the display server | | `extra_skills_feature` | Extra OVOS skills enabled during the installation | | `gui_feature` | GUI enabled during the installation | @@ -42,6 +43,8 @@ The table below lists the collected data. See the [Ansible task](https://github. | `raspberry_pi` | Whether OVOS was installed on a Raspberry Pi | | `skills_feature` | Default OVOS skills enabled during the installation | | `sound_server` | Whether PulseAudio or PipeWire is used | +| `stt_engine` | `local` or `public`: where speech recognition runs | +| `tts_engine` | `local` or `public`: where the assistant's voice is made | | `tuning_enabled` | Whether the Raspberry Pi tuning feature was used | | `venv` | OVOS installed into a Python virtual environment | diff --git a/scripts/sync_translations.py b/scripts/sync_translations.py index 5b63780c..6f933e6c 100644 --- a/scripts/sync_translations.py +++ b/scripts/sync_translations.py @@ -77,8 +77,13 @@ SUMMARY_STATE_UNSUPPORTED_PROFILE="{state_unsupported_profile}" SUMMARY_STATE_MISSING_URL="{state_missing_url}" SUMMARY_STATE_MISSING_CONFIGURATION="{state_missing_configuration}" +SUMMARY_SPEECH_LOCAL="{speech_local}" +SUMMARY_SPEECH_PUBLIC="{speech_public}" +SUMMARY_SPEECH_PUBLIC_HARDWARE="{speech_public_hardware}" +SUMMARY_SPEECH_PUBLIC_SETUP="{speech_public_setup}" +SUMMARY_SPEECH_UNUSED="{speech_unused}" -export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION +export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED """ FEATURES_TEMPLATE = """ @@ -96,6 +101,18 @@ export CONTENT TITLE SKILL_DESCRIPTION EXTRA_SKILL_DESCRIPTION GUI_DESCRIPTION HOMEASSISTANT_DESCRIPTION LLM_DESCRIPTION """ +SPEECH_TEMPLATE = """ +#!/usr/bin/env bash +CONTENT=" +{content} +" +TITLE="{title}" +LOCAL_DESCRIPTION="{local_description}" +PUBLIC_DESCRIPTION="{public_description}" + +export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION +""" + SAT_TEMPLATE = """ #!/usr/bin/env bash # Global message @@ -179,6 +196,7 @@ "overclock.sh": OVERCLOCK_TEMPLATE, "profiles.sh": TEMPLATE, "satellite.sh": SAT_TEMPLATE, + "speech.sh": SPEECH_TEMPLATE, "summary.sh": SUMMARY_TEMPLATE, "telemetry.sh": TEMPLATE, "tuning.sh": TEMPLATE, diff --git a/setup.sh b/setup.sh index b043acee..fb721612 100644 --- a/setup.sh +++ b/setup.sh @@ -45,6 +45,9 @@ source utils/banner.sh # shellcheck source=utils/common.sh source utils/common.sh +# shellcheck source=utils/speech.sh +source utils/speech.sh + # # shellcheck source=utils/argparse.sh source utils/argparse.sh @@ -86,6 +89,7 @@ required_packages check_python_compatibility detect_sound detect_display +detect_local_speech_support create_python_venv install_ansible detect_scenario @@ -131,6 +135,7 @@ if [ "$EXISTING_INSTANCE" == "true" ]; then fi normalize_feature_gui_support +normalize_speech_engine log_info "➤ Starting Ansible playbook... ☕🍵🧋" @@ -250,6 +255,8 @@ ansible_command=( -e "ovos_installer_satellite_key=${SATELLITE_KEY}" \ -e "ovos_installer_satellite_password=${SATELLITE_PASSWORD}" \ -e "ovos_installer_cpu_is_capable=${CPU_IS_CAPABLE}" \ + -e "ovos_installer_local_speech_capable=${LOCAL_SPEECH_CAPABLE:-false}" \ + -e "ovos_installer_speech_engine=${SPEECH_ENGINE:-public}" \ -e "ovos_installer_cleaning=${ansible_cleaning}" \ -e "ovos_installer_display_server=${DISPLAY_SERVER}" \ -e "ovos_installer_telemetry=${SHARE_TELEMETRY}" \ diff --git a/tests/bats/code_quality.bats b/tests/bats/code_quality.bats index 8a0b7f86..9463dba8 100644 --- a/tests/bats/code_quality.bats +++ b/tests/bats/code_quality.bats @@ -2751,7 +2751,7 @@ YAML run grep -q "SCENARIO_ALLOWED_FEATURES=(skills extra_skills gui homeassistant llm)" utils/constants.sh assert_success - run grep -q "SCENARIO_ALLOWED_OPTIONS=(features channel hardware share_telemetry share_usage_telemetry profile method uninstall raspberry_pi_tuning hivemind llm)" utils/constants.sh + run grep -q "SCENARIO_ALLOWED_OPTIONS=(features channel hardware share_telemetry share_usage_telemetry profile method uninstall raspberry_pi_tuning hivemind llm speech_engine)" utils/constants.sh assert_success run grep -q "SCENARIO_ALLOWED_LLM_OPTIONS=(api_url key model persona max_tokens temperature top_p)" utils/constants.sh @@ -3335,7 +3335,7 @@ function anchors_of() { run grep -q 'export HARDWARE_CONFIRMATION="\${HARDWARE_CONFIRMATION:-}"' utils/argparse.sh assert_success - run grep -q 'declare -ra SCENARIO_ALLOWED_OPTIONS=(features channel hardware share_telemetry share_usage_telemetry profile method uninstall raspberry_pi_tuning hivemind llm)' utils/constants.sh + run grep -q 'declare -ra SCENARIO_ALLOWED_OPTIONS=(features channel hardware share_telemetry share_usage_telemetry profile method uninstall raspberry_pi_tuning hivemind llm speech_engine)' utils/constants.sh assert_success run grep -q 'hardware)' utils/scenario.sh @@ -5115,3 +5115,51 @@ for template in ('core-requirements.txt.j2', 'satellite-requirements.txt.j2'): run bash -c "grep -A14 -F '\"module\": \"ovos-ww-plugin-precise-onnx\",' '$conf_file' | grep -q '\"model\": \"https://'" assert_success } + +@test "local_speech_is_wired_from_requirements_to_telemetry" { + # The plugins are only installed when local speech was chosen, and onnx-asr + # needs its hub extra: without huggingface_hub it cannot fetch a model at all. + local requirements + for requirements in core-requirements satellite-requirements; do + local file="ansible/roles/ovos_virtualenv/templates/virtualenv/${requirements}.txt.j2" + run bash -c "awk \"/ovos_installer_speech_engine/,/endif/\" '$file'" + assert_output --partial "ovos-stt-plugin-onnx-asr" + assert_output --partial "onnx-asr[cpu,hub]" + assert_output --partial "phoonnx" + done + + # The models come from the ovos-config installed in the virtualenv, so the + # speech configuration runs after the install and only for local speech. + local venv_tasks="ansible/roles/ovos_virtualenv/tasks/venv.yml" + run grep -q "tasks_from: speech.yml" "$venv_tasks" + assert_success + run bash -c "awk '/Configure local speech/,0' '$venv_tasks' | grep -q \"ovos_installer_speech_engine | default('public')) == 'local'\"" + assert_success + + # Only the stt and tts sections are written, the public STT server stays as the + # fallback, and the models are fetched during the install. + local speech_tasks="ansible/roles/ovos_config/tasks/speech.yml" + run grep -q "speech_recommendation.py" "$speech_tasks" + assert_success + run grep -q "'fallback_module': ovos_config_speech_stt_fallback" "$speech_tasks" + assert_success + run grep -q "speech_prefetch.py" "$speech_tasks" + assert_success + run grep -q "^ovos_config_speech_stt_fallback: ovos-stt-plugin-server$" ansible/roles/ovos_config/defaults/main.yml + assert_success + + # A re-run keeps the sections the last run wrote instead of dropping them. + run grep -q "ovos_config_speech_current" ansible/roles/ovos_config/templates/mycroft.conf.j2 + assert_success + + # Telemetry reports where speech ended up, and uninstall removes the models. + local telemetry="ansible/roles/ovos_telemetry/tasks/main.yml" + run grep -q 'stt_engine: "{{ ovos_telemetry_stt_engine }}"' "$telemetry" + assert_success + run grep -q 'tts_engine: "{{ ovos_telemetry_tts_engine }}"' "$telemetry" + assert_success + run grep -q "local_speech_capable:" "$telemetry" + assert_success + run grep -q "/.local/share/ovos_stt_plugin_onnxasr" ansible/roles/ovos_services/defaults/main.yml + assert_success +} diff --git a/tests/bats/locales.bats b/tests/bats/locales.bats index 1e09a774..ac22f990 100644 --- a/tests/bats/locales.bats +++ b/tests/bats/locales.bats @@ -129,6 +129,52 @@ function setup() { done } +@test "locales_speech_scripts_are_complete_and_sourceable" { + for locale_dir in tui/locales/*; do + local locale_file="$locale_dir/speech.sh" + + if [ ! -f "$locale_file" ]; then + echo "Missing speech locale: $locale_file" >&2 + return 1 + fi + + run bash -euc " + source '$locale_file' + test -n \"\$TITLE\" + test -n \"\$CONTENT\" + test -n \"\$LOCAL_DESCRIPTION\" + test -n \"\$PUBLIC_DESCRIPTION\" + " + assert_success + done +} + +@test "locales_summary_scripts_show_where_speech_runs" { + for f in tui/locales/*/summary.sh; do + run bash -euc " + METHOD='virtualenv' + CHANNEL='alpha' + PROFILE='ovos' + SPEECH_SUMMARY_STATE='speech-state-marker' + BACK_BUTTON='Back' + source '$f' + test -n \"\$SUMMARY_SPEECH_LOCAL\" + test -n \"\$SUMMARY_SPEECH_PUBLIC\" + test -n \"\$SUMMARY_SPEECH_PUBLIC_HARDWARE\" + test -n \"\$SUMMARY_SPEECH_PUBLIC_SETUP\" + test -n \"\$SUMMARY_SPEECH_UNUSED\" + printf '%s\\n' \"\$CONTENT\" + " + + if [ "$status" -ne 0 ]; then + echo "Failed to source $f" >&2 + echo "$output" >&2 + return 1 + fi + assert_output --partial "speech-state-marker" + done +} + @test "locales_usage_telemetry_scripts_are_complete_and_sourceable" { for locale_dir in tui/locales/*; do local locale_file="$locale_dir/usage_telemetry.sh" @@ -315,6 +361,26 @@ function setup() { done } +@test "locales_speech_strings_are_localized_outside_en_us" { + for f in tui/locales/*/speech.sh; do + if [ "$f" = "tui/locales/en-us/speech.sh" ]; then + continue + fi + + run bash -euc " + source tui/locales/en-us/speech.sh + en_content=\$CONTENT + en_local_description=\$LOCAL_DESCRIPTION + en_public_description=\$PUBLIC_DESCRIPTION + source '$f' + [ \"\$CONTENT\" != \"\$en_content\" ] + [ \"\$LOCAL_DESCRIPTION\" != \"\$en_local_description\" ] + [ \"\$PUBLIC_DESCRIPTION\" != \"\$en_public_description\" ] + " + assert_success + done +} + @test "english_llm_locale_explains reply tuning in plain language" { local file="tui/locales/en-us/llm.sh" diff --git a/tests/bats/speech.bats b/tests/bats/speech.bats new file mode 100644 index 00000000..b7497e95 --- /dev/null +++ b/tests/bats/speech.bats @@ -0,0 +1,270 @@ +#!/usr/bin/env bats +# +# Local speech is offered on a Raspberry Pi 5 with 8 GB or a machine at least as +# capable, on alpha virtualenv installs only. Everything else stays on the +# public servers, whatever was asked for. + +function setup() { + load "$HOME/shell-testing/test_helper/bats-support/load" + load "$HOME/shell-testing/test_helper/bats-assert/load" + load ../../utils/constants.sh + load ../../utils/speech.sh + + LOG_FILE="$(mktemp)" + + # A 64-bit Linux host with AVX2 and 16 GB, which can run local speech. + CPU_IS_CAPABLE="true" + RASPBERRYPI_MODEL="N/A" + TOTAL_MEMORY_MB=16000 + MOCK_UNAME_S="Linux" + MOCK_UNAME_M="x86_64" + MOCK_LONG_BIT="64" + + function uname() { + case "$1" in + -s) printf '%s\n' "$MOCK_UNAME_S" ;; + -m) printf '%s\n' "$MOCK_UNAME_M" ;; + esac + } + function getconf() { + printf '%s\n' "$MOCK_LONG_BIT" + } +} + +function teardown() { + rm -f "$LOG_FILE" + unset -f uname getconf sysctl +} + +@test "speech: a capable 64-bit Linux machine with 16 GB can run local speech" { + run local_speech_hardware_supported + assert_success +} + +@test "speech: a Raspberry Pi 5 with 8 GB can run local speech" { + MOCK_UNAME_M="aarch64" + RASPBERRYPI_MODEL="Raspberry Pi 5 Model B Rev 1.0" + # What an 8 GB board reports once firmware and kernel have taken their share. + TOTAL_MEMORY_MB=8052 + + run local_speech_hardware_supported + assert_success +} + +@test "speech: the Pi 500 and the Compute Module 5 count as a Pi 5" { + MOCK_UNAME_M="aarch64" + TOTAL_MEMORY_MB=8052 + + RASPBERRYPI_MODEL="Raspberry Pi 500 Rev 1.0" + run local_speech_hardware_supported + assert_success + + RASPBERRYPI_MODEL="Raspberry Pi Compute Module 5 Rev 1.0" + run local_speech_hardware_supported + assert_success +} + +@test "speech: a Raspberry Pi 5 with 4 GB stays public" { + MOCK_UNAME_M="aarch64" + RASPBERRYPI_MODEL="Raspberry Pi 5 Model B Rev 1.0" + TOTAL_MEMORY_MB=4040 + + run local_speech_hardware_supported + assert_failure +} + +@test "speech: a Raspberry Pi 4 stays public even with 8 GB" { + MOCK_UNAME_M="aarch64" + RASPBERRYPI_MODEL="Raspberry Pi 4 Model B Rev 1.5" + TOTAL_MEMORY_MB=7810 + + run local_speech_hardware_supported + assert_failure +} + +@test "speech: the memory floor is 7.5 GiB" { + TOTAL_MEMORY_MB=7680 + run local_speech_hardware_supported + assert_success + + TOTAL_MEMORY_MB=7679 + run local_speech_hardware_supported + assert_failure +} + +@test "speech: a CPU without AVX2 or NEON stays public" { + CPU_IS_CAPABLE="false" + + run local_speech_hardware_supported + assert_failure +} + +@test "speech: a 32-bit userland stays public" { + MOCK_UNAME_M="aarch64" + MOCK_LONG_BIT="32" + RASPBERRYPI_MODEL="Raspberry Pi 5 Model B Rev 1.0" + TOTAL_MEMORY_MB=8052 + + run local_speech_hardware_supported + assert_failure +} + +@test "speech: an Intel Mac stays public and an Apple silicon Mac does not" { + MOCK_UNAME_S="Darwin" + + MOCK_UNAME_M="x86_64" + run local_speech_hardware_supported + assert_failure + + MOCK_UNAME_M="arm64" + run local_speech_hardware_supported + assert_success +} + +@test "speech: memory on a Mac is read from hw.memsize" { + MOCK_UNAME_S="Darwin" + function sysctl() { + printf '%s\n' "17179869184" + } + + run total_memory_mb + assert_success + assert_output "16384" +} + +@test "speech: detection exports whether this host is capable" { + MOCK_UNAME_S="Darwin" + MOCK_UNAME_M="arm64" + function sysctl() { + printf '%s\n' "8589934592" + } + + detect_local_speech_support >/dev/null + assert_equal "$TOTAL_MEMORY_MB" "8192" + assert_equal "$LOCAL_SPEECH_CAPABLE" "true" + + function sysctl() { + printf '%s\n' "4294967296" + } + detect_local_speech_support >/dev/null + assert_equal "$LOCAL_SPEECH_CAPABLE" "false" +} + +@test "speech: local is only available on alpha virtualenv installs with audio" { + LOCAL_SPEECH_CAPABLE="true" + CHANNEL="alpha" + METHOD="virtualenv" + PROFILE="ovos" + run local_speech_available + assert_success + + PROFILE="satellite" + run local_speech_available + assert_success + + PROFILE="server" + run local_speech_available + assert_failure + + PROFILE="ovos" + CHANNEL="testing" + run local_speech_available + assert_failure + + CHANNEL="alpha" + METHOD="containers" + run local_speech_available + assert_failure + + METHOD="virtualenv" + LOCAL_SPEECH_CAPABLE="false" + run local_speech_available + assert_failure +} + +@test "speech: an available local choice is kept" { + LOCAL_SPEECH_CAPABLE="true" + CHANNEL="alpha" + METHOD="virtualenv" + PROFILE="ovos" + SPEECH_ENGINE="local" + + normalize_speech_engine + assert_equal "$SPEECH_ENGINE" "local" +} + +@test "speech: local where it is not available falls back to public and says so" { + LOCAL_SPEECH_CAPABLE="true" + CHANNEL="testing" + METHOD="virtualenv" + PROFILE="ovos" + SPEECH_ENGINE="local" + + run normalize_speech_engine + assert_success + assert_output --partial "Using the public servers instead" + + normalize_speech_engine >/dev/null + assert_equal "$SPEECH_ENGINE" "public" +} + +@test "speech: nothing chosen means public" { + unset SPEECH_ENGINE + LOCAL_SPEECH_CAPABLE="true" + CHANNEL="alpha" + METHOD="virtualenv" + PROFILE="ovos" + + run normalize_speech_engine + assert_output "" + + normalize_speech_engine + assert_equal "$SPEECH_ENGINE" "public" +} + +function run_scenario_with_speech_engine() { + local value="$1" + local scenario_file yq_mock + scenario_file="$(mktemp)" + yq_mock="$(mktemp)" + + printf 'speech_engine: %s\n' "$value" >"$scenario_file" + { + printf '%s\n' '#!/usr/bin/env bash' + printf '%s\n' 'if [ "$1" = '"'"'to_entries | map([.key, .value] | join("=")) | .[]'"'"' ]; then' + printf '%s\n' ' printf "%s\n" uninstall=false method=virtualenv channel=alpha profile=ovos features=enabled raspberry_pi_tuning=false share_telemetry=false share_usage_telemetry=false' + printf ' printf "%%s\\n" speech_engine=%s\n' "$value" + printf '%s\n' 'elif [ "$1" = '"'"'.features | to_entries | map([.key, .value] | join("=")) | .[]'"'"' ]; then' + printf '%s\n' ' printf "%s\n" skills=true' + printf '%s\n' 'fi' + } >"$yq_mock" + chmod +x "$yq_mock" + + run bash -c ' + cd "$1" + source utils/constants.sh + source utils/common.sh + export SCENARIO_PATH="$2" + export YQ_BINARY_PATH="$3" + source utils/scenario.sh + printf "%s|%s|%s\n" "${SPEECH_ENGINE:-unset}" "${SCENARIO_NOT_SUPPORTED:-false}" "${SCENARIO_ERROR:-}" + ' bash "$BATS_TEST_DIRNAME/../.." "$scenario_file" "$yq_mock" + + rm -f "$scenario_file" "$yq_mock" +} + +@test "speech: a scenario can ask for local or public speech" { + run_scenario_with_speech_engine "local" + assert_success + assert_output "local|false|" + + run_scenario_with_speech_engine "public" + assert_success + assert_output "public|false|" +} + +@test "speech: a scenario with an unknown speech engine is rejected" { + run_scenario_with_speech_engine "offline" + assert_success + assert_output "unset|true|speech_engine: offline (expected: local, public)" +} diff --git a/tests/bats/tui_navigation.bats b/tests/bats/tui_navigation.bats index e27bc787..382498b7 100644 --- a/tests/bats/tui_navigation.bats +++ b/tests/bats/tui_navigation.bats @@ -702,3 +702,77 @@ function rendered_titles() { run sort -u "$WHIPTAIL_BACKTITLE_LOG" assert_output "Back: previous screen - keep going back to leave" } + +@test "navigation: the speech question is only asked where local speech is available" { + source tui/navigation.sh + + LOCAL_SPEECH_CAPABLE="true" + CHANNEL="alpha" + METHOD="virtualenv" + PROFILE="ovos" + run tui_flow_step_enabled speech + assert_success + + CHANNEL="testing" + run tui_flow_step_enabled speech + assert_failure + + CHANNEL="alpha" + LOCAL_SPEECH_CAPABLE="false" + run tui_flow_step_enabled speech + assert_failure +} + +@test "tui: capable hardware on alpha is asked where speech runs" { + export LOCAL_SPEECH_CAPABLE="true" + WHIPTAIL_PREFERRED_TAGS="alpha" + + # shellcheck source=tui/main.sh + source tui/main.sh + + run rendered_titles + assert_line --index 5 "Open Voice OS Installation - Features" + assert_line --index 6 "Open Voice OS Installation - Speech" + assert_line --index 7 "Open Voice OS Installation - Summary" + + # Local is what the hardware was checked for, so it is preselected. + assert_equal "$SPEECH_ENGINE" "local" + assert_equal "$(jq -r '.speech_engine' "$INSTALLER_STATE_FILE")" "local" +} + +@test "tui: back on the summary screen returns to the speech question" { + export LOCAL_SPEECH_CAPABLE="true" + WHIPTAIL_PREFERRED_TAGS="alpha" + # 6 features, 7 speech, 8 summary. + cancel_on_call 8 + + # shellcheck source=tui/main.sh + source tui/main.sh + + run rendered_titles + assert_line --index 7 "Open Voice OS Installation - Summary" + assert_line --index 8 "Open Voice OS Installation - Speech" + assert_line --index 9 "Open Voice OS Installation - Summary" +} + +@test "tui: choosing the public servers is kept" { + export LOCAL_SPEECH_CAPABLE="true" + WHIPTAIL_PREFERRED_TAGS="alpha public" + + # shellcheck source=tui/main.sh + source tui/main.sh + + assert_equal "$SPEECH_ENGINE" "public" + assert_equal "$(jq -r '.speech_engine' "$INSTALLER_STATE_FILE")" "public" +} + +@test "tui: the testing channel is never asked about speech" { + export LOCAL_SPEECH_CAPABLE="true" + + # shellcheck source=tui/main.sh + source tui/main.sh + + assert_equal "$CHANNEL" "testing" + run rendered_titles + refute_line "Open Voice OS Installation - Speech" +} diff --git a/translations/ca-es/strings.json b/translations/ca-es/strings.json index ebe70a69..115fc6b9 100644 --- a/translations/ca-es/strings.json +++ b/translations/ca-es/strings.json @@ -56,9 +56,20 @@ "content_key": "Introduïu la clau d'escolta de HiveMind relacionada amb el satèl·lit:", "content_password": "Introduïu la contrasenya d'escolta de HiveMind relacionada amb el satèl·lit:" }, + "speech.sh": { + "title": "Instal·lació de l'Open Voice OS - Veu", + "content": "L'Open Voice OS converteix el que dius en text (reconeixement de veu) i llegeix les respostes en veu alta (síntesi de veu). Aquest equip és prou potent per fer totes dues coses ell mateix.\n\n- local: tot s'executa en aquest equip. La teva veu es queda aquí i res no depèn d'internet ni de com d'ocupats estiguin els servidors. L'instal·lador baixa els models de veu del teu idioma, des d'uns quants centenars de megabytes fins a uns quants gigabytes, i el reconeixement manté ocupat el processador mentre parles.\n- public: els servidors públics de l'Open Voice OS fan la feina. No cal baixar res, però cada petició viatja per internet fins a servidors que comparteix tota la comunitat, cosa que hi afegeix retard de xarxa i que s'alenteix quan molta gent els fa servir alhora. Les teves gravacions i les respostes es processen en aquests servidors.\n\nSi el reconeixement local falla o no sent res, aquella gravació s'envia als servidors públics.\n\nSelecciona on es processa la veu, si us plau:", + "local_description": "Processar la veu en aquest equip", + "public_description": "Servidors públics de l'Open Voice OS" + }, "summary.sh": { "title": "Instal·lació de l'Open VoiceOS - Resum", - "content": "Gairebé heu acabat, aquí teniu un resum de les opcions que heu triat en instal·lar l'Open Voice OS:\n\n - Mètode: ${METHOD:-}\n - Versió: ${CHANNEL:-}\n - Perfil: ${PROFILE:-}\n - Habilitats: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Habilitats extra: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Afinació: ${TUNING_SUMMARY_STATE:-}\n\nLes opcions seleccionades durant el procés d'instal·lació de l'Open Voice OS s'han considerat acuradament per a adaptar el nostre sistema a les vostres necessitats i preferències úniques.\n\nUs sembla correcte aquest resum? Si no, selecciona ${BACK_BUTTON:-} per tornar enrere i fer canvis." + "content": "Gairebé heu acabat, aquí teniu un resum de les opcions que heu triat en instal·lar l'Open Voice OS:\n\n - Mètode: ${METHOD:-}\n - Versió: ${CHANNEL:-}\n - Perfil: ${PROFILE:-}\n - Habilitats: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Habilitats extra: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Veu: ${SPEECH_SUMMARY_STATE:-}\n - Afinació: ${TUNING_SUMMARY_STATE:-}\n\nLes opcions seleccionades durant el procés d'instal·lació de l'Open Voice OS s'han considerat acuradament per a adaptar el nostre sistema a les vostres necessitats i preferències úniques.\n\nUs sembla correcte aquest resum? Si no, selecciona ${BACK_BUTTON:-} per tornar enrere i fer canvis.", + "speech_local": "en aquest equip", + "speech_public": "servidors públics", + "speech_public_hardware": "servidors públics (local: Pi 5 de 8 GB o més)", + "speech_public_setup": "servidors públics (local: alpha i virtualenv)", + "speech_unused": "aquest perfil no la fa servir" }, "telemetry.sh": { "title": "Instal·lació de l'Open Voice OS - Telemetria", diff --git a/translations/da/strings.json b/translations/da/strings.json index 14f83dce..af552ac7 100644 --- a/translations/da/strings.json +++ b/translations/da/strings.json @@ -56,9 +56,20 @@ "content_key": "Indtast venligst HiveMind-lytternøglen relateret til satellitten:", "content_password": "Indtast venligst HiveMind-lytteradgangskoden relateret til satellitten:" }, + "speech.sh": { + "title": "Open Voice OS Installation - Tale", + "content": "Open Voice OS omsætter det, du siger, til tekst (talegenkendelse) og læser sine svar højt (talesyntese). Denne maskine er kraftig nok til selv at klare begge dele.\n\n- local: alt kører på denne maskine. Din stemme bliver her, og intet afhænger af internettet eller af, hvor travlt serverne har. Installationsprogrammet henter talemodellerne til dit sprog, fra et par hundrede megabyte til et par gigabyte, og genkendelsen holder processoren beskæftiget, mens du taler.\n- public: de offentlige Open Voice OS-servere klarer arbejdet. Der skal ikke hentes noget, men hver forespørgsel går over internettet til servere, som hele fællesskabet deler. Det giver netværksforsinkelse og bliver langsommere, når mange bruger dem på samme tid. Dine optagelser og svarene behandles på de servere.\n\nHvis den lokale genkendelse fejler eller ikke hører noget, sendes netop den optagelse til de offentlige servere i stedet.\n\nVælg venligst, hvor tale skal behandles:", + "local_description": "Behandl tale på denne maskine", + "public_description": "Brug de offentlige Open Voice OS-servere" + }, "summary.sh": { "title": "Open Voice OS Installation - Resume", - "content": "Du er næsten færdig, her er en oversigt over de valg, du har truffet for at installere Open Voice OS:\n\n - Metode: ${METHOD:-}\n - Version: ${CHANNEL:-}\n - Profil: ${PROFILE:-}\n - Færdigheder: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Ekstra færdigheder: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Tuning: ${TUNING_SUMMARY_STATE:-}\n\nDe valg, der blev truffet under installationen af ​​Open Voice OS, er blevet nøje overvejet for at skræddersy vores system til dine unikke behov og præferencer.\n\nSer denne oversigt korrekt ud for dig? Hvis ikke, vælg ${BACK_BUTTON:-} for at gå tilbage og foretage ændringer." + "content": "Du er næsten færdig, her er en oversigt over de valg, du har truffet for at installere Open Voice OS:\n\n - Metode: ${METHOD:-}\n - Version: ${CHANNEL:-}\n - Profil: ${PROFILE:-}\n - Færdigheder: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Ekstra færdigheder: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Tale: ${SPEECH_SUMMARY_STATE:-}\n - Tuning: ${TUNING_SUMMARY_STATE:-}\n\nDe valg, der blev truffet under installationen af ​​Open Voice OS, er blevet nøje overvejet for at skræddersy vores system til dine unikke behov og præferencer.\n\nSer denne oversigt korrekt ud for dig? Hvis ikke, vælg ${BACK_BUTTON:-} for at gå tilbage og foretage ændringer.", + "speech_local": "på denne maskine", + "speech_public": "offentlige servere", + "speech_public_hardware": "offentlige servere (lokal: Pi 5 med 8 GB eller mere)", + "speech_public_setup": "offentlige servere (lokal: alpha og virtualenv)", + "speech_unused": "bruges ikke af denne profil" }, "telemetry.sh": { "title": "Open Voice OS Installation - Telemetri", diff --git a/translations/de-de/strings.json b/translations/de-de/strings.json index 24e18956..a0af879b 100644 --- a/translations/de-de/strings.json +++ b/translations/de-de/strings.json @@ -56,9 +56,20 @@ "content_key": "Geben Sie den Zugangsschlüssel des Satelliten für den HiveMind Server ein:", "content_password": "Geben Sie das Passwort des Satelliten für den HiveMind Server ein:" }, + "speech.sh": { + "title": "Open Voice OS-Installation - Sprachverarbeitung", + "content": "Open Voice OS wandelt Gesprochenes in Text um (Spracherkennung) und liest seine Antworten vor (Sprachsynthese). Dieser Rechner ist leistungsfähig genug, beides selbst zu erledigen.\n\n- local: Alles läuft auf diesem Rechner. Ihre Stimme bleibt hier, und nichts hängt vom Internet oder von der Auslastung der Server ab. Der Installer lädt die Sprachmodelle für Ihre Sprache herunter, von einigen hundert Megabyte bis zu ein paar Gigabyte, und die Erkennung lastet den Prozessor aus, während Sie sprechen.\n- public: Die öffentlichen Open Voice OS-Server übernehmen die Arbeit. Es muss nichts heruntergeladen werden, aber jede Anfrage geht über das Internet an Server, die sich die ganze Community teilt. Das kostet Netzwerklaufzeit und wird langsamer, wenn viele sie gleichzeitig nutzen. Ihre Aufnahmen und die Antworten werden auf diesen Servern verarbeitet.\n\nWenn die lokale Erkennung fehlschlägt oder nichts hört, wird genau diese Aufnahme stattdessen an die öffentlichen Server geschickt.\n\nBitte wählen Sie, wo die Sprache verarbeitet wird:", + "local_description": "Sprache auf diesem Rechner verarbeiten", + "public_description": "Öffentliche Open Voice OS-Server nutzen" + }, "summary.sh": { "title": "Open Voice OS Installation - Zusammenfassung", - "content": "Fast fertig. Eine kurze Zusammenfassung:\n\n - Umgebung: ${METHOD:-}\n - Version: ${CHANNEL:-}\n - Profil: ${PROFILE:-}\n - Skills: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Extra-Skills: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Tuning: ${TUNING_SUMMARY_STATE:-}\n\nDie Entscheidungen, die während des Installationsprozesses von Open Voice OS getroffen werden, wurden sorgfältig abgewogen, um unser System an Ihre individuellen Bedürfnisse und Vorlieben anzupassen.\n\nStimmen die Einstellungen? Falls nicht, wählen Sie ${BACK_BUTTON:-}, um zurückzugehen und Änderungen vorzunehmen." + "content": "Fast fertig. Eine kurze Zusammenfassung:\n\n - Umgebung: ${METHOD:-}\n - Version: ${CHANNEL:-}\n - Profil: ${PROFILE:-}\n - Skills: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Extra-Skills: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Sprache: ${SPEECH_SUMMARY_STATE:-}\n - Tuning: ${TUNING_SUMMARY_STATE:-}\n\nDie Entscheidungen, die während des Installationsprozesses von Open Voice OS getroffen werden, wurden sorgfältig abgewogen, um unser System an Ihre individuellen Bedürfnisse und Vorlieben anzupassen.\n\nStimmen die Einstellungen? Falls nicht, wählen Sie ${BACK_BUTTON:-}, um zurückzugehen und Änderungen vorzunehmen.", + "speech_local": "auf diesem Rechner", + "speech_public": "öffentliche Server", + "speech_public_hardware": "öffentliche Server (lokal: Pi 5 mit 8 GB oder besser)", + "speech_public_setup": "öffentliche Server (lokal: alpha und virtualenv)", + "speech_unused": "von diesem Profil nicht genutzt" }, "telemetry.sh": { "title": "Open Voice OS Installation - Telemetrie", diff --git a/translations/en-us/strings.json b/translations/en-us/strings.json index eea5f091..c738c034 100644 --- a/translations/en-us/strings.json +++ b/translations/en-us/strings.json @@ -56,14 +56,25 @@ "content_key": "Please enter the HiveMind listener key related to the satellite:", "content_password": "Please enter the HiveMind listener password related to the satellite:" }, + "speech.sh": { + "title": "Open Voice OS Installation - Speech", + "content": "Open Voice OS turns what you say into text (speech recognition) and reads its answers out loud (speech synthesis). This machine is powerful enough to do both itself.\n\n - local: everything runs on this machine. Your voice stays here, and nothing depends on the internet or on how busy the servers are. The installer downloads the speech models for your language, from a few hundred megabytes to a few gigabytes, and recognition keeps the processor busy while you talk.\n - public: the Open Voice OS public servers do the work. Nothing to download, but every request travels over the internet to servers the whole community shares, which adds network delay and slows down when many people use them at once. Your recordings and the answers are processed on those servers.\n\nWhen local recognition fails or hears nothing, that one recording is sent to the public servers instead.\n\nPlease select where speech is processed:", + "local_description": "Run speech on this machine", + "public_description": "Use the Open Voice OS public servers" + }, "summary.sh": { "title": "Open Voice OS Installation - Summary", - "content": "You are almost done, here is a summary of choices you made to install Open Voice OS:\n\n - Method: ${METHOD:-}\n - Version: ${CHANNEL:-}\n - Profile: ${PROFILE:-}\n - Skills: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Extra: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - HA: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Tuning: ${TUNING_SUMMARY_STATE:-}\n\nThe choices made during the Open Voice OS installation process have been carefully considered to tailor our system to your unique needs and preferences.\n\nDoes this summary look correct to you? If not, select ${BACK_BUTTON:-} to go back and make changes.", + "content": "You are almost done, here is a summary of choices you made to install Open Voice OS:\n\n - Method: ${METHOD:-}\n - Version: ${CHANNEL:-}\n - Profile: ${PROFILE:-}\n - Skills: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Extra: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - HA: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Speech: ${SPEECH_SUMMARY_STATE:-}\n - Tuning: ${TUNING_SUMMARY_STATE:-}\n\nThe choices made during the Open Voice OS installation process have been carefully considered to tailor our system to your unique needs and preferences.\n\nDoes this summary look correct to you? If not, select ${BACK_BUTTON:-} to go back and make changes.", "state_enabled": "enabled", "state_disabled": "disabled", "state_unsupported_profile": "selected (not supported for this profile)", "state_missing_url": "selected (missing URL; will be skipped)", - "state_missing_configuration": "selected (missing configuration; will be skipped)" + "state_missing_configuration": "selected (missing configuration; will be skipped)", + "speech_local": "on this machine", + "speech_public": "public servers", + "speech_public_hardware": "public servers (local needs a Pi 5 with 8 GB or better)", + "speech_public_setup": "public servers (local needs alpha and virtualenv)", + "speech_unused": "not used by this profile" }, "telemetry.sh": { "title": "Open Voice OS Installation - Telemetry", diff --git a/translations/es-es/strings.json b/translations/es-es/strings.json index dfe89e0b..8defb709 100644 --- a/translations/es-es/strings.json +++ b/translations/es-es/strings.json @@ -56,9 +56,20 @@ "content_key": "Introduce la clave de escucha HiveMind del satélite:", "content_password": "Introduce la contraseña de escucha HiveMind del satélite:" }, + "speech.sh": { + "title": "Instalación de Open Voice OS - Voz", + "content": "Open Voice OS convierte lo que dice en texto (reconocimiento de voz) y lee sus respuestas en voz alta (síntesis de voz). Este equipo es lo bastante potente para hacer ambas cosas por sí mismo.\n\n- local: todo se ejecuta en este equipo. Su voz se queda aquí y nada depende de internet ni de lo ocupados que estén los servidores. El instalador descarga los modelos de voz de su idioma, desde unos cientos de megabytes hasta unos pocos gigabytes, y el reconocimiento mantiene ocupado el procesador mientras habla.\n- public: los servidores públicos de Open Voice OS hacen el trabajo. No hay nada que descargar, pero cada petición viaja por internet hasta servidores que comparte toda la comunidad, lo que añade retardo de red y se vuelve más lento cuando muchas personas los usan a la vez. Sus grabaciones y las respuestas se procesan en esos servidores.\n\nSi el reconocimiento local falla o no oye nada, esa grabación se envía a los servidores públicos.\n\nSeleccione dónde se procesa la voz:", + "local_description": "Procesar la voz en este equipo", + "public_description": "Usar los servidores públicos de Open Voice OS" + }, "summary.sh": { "title": "Instalación de Open Voice OS - Resumen", - "content": "¡Ya casi has terminado! Aquí tienes un resumen de las opciones que has elegido para instalar Open Voice OS:\n\n- Método: ${METHOD:-}\n- Versión: ${CHANNEL:-}\n- Perfil: ${PROFILE:-}\n- Habilidades: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n- Extras: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n- Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n- LLM: ${LLM_SUMMARY_STATE:-}\n- Ajustes: ${TUNING_SUMMARY_STATE:-}\n\nLas decisiones que has tomado durante el proceso de instalación de Open Voice OS han sido cuidadosamente consideradas para adaptar el sistema a tus necesidades y preferencias.\n\n¿Este resumen es correcto? Si no, selecciona ${BACK_BUTTON:-} para volver atrás y hacer cambios." + "content": "¡Ya casi has terminado! Aquí tienes un resumen de las opciones que has elegido para instalar Open Voice OS:\n\n- Método: ${METHOD:-}\n- Versión: ${CHANNEL:-}\n- Perfil: ${PROFILE:-}\n- Habilidades: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n- Extras: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n- Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n- LLM: ${LLM_SUMMARY_STATE:-}\n- Voz: ${SPEECH_SUMMARY_STATE:-}\n- Ajustes: ${TUNING_SUMMARY_STATE:-}\n\nLas decisiones que has tomado durante el proceso de instalación de Open Voice OS han sido cuidadosamente consideradas para adaptar el sistema a tus necesidades y preferencias.\n\n¿Este resumen es correcto? Si no, selecciona ${BACK_BUTTON:-} para volver atrás y hacer cambios.", + "speech_local": "en este equipo", + "speech_public": "servidores públicos", + "speech_public_hardware": "servidores públicos (local: Pi 5 con 8 GB o más)", + "speech_public_setup": "servidores públicos (local: alpha y virtualenv)", + "speech_unused": "este perfil no la usa" }, "telemetry.sh": { "title": "Instalación de Open Voice OS - Telemetría", diff --git a/translations/eu-es/strings.json b/translations/eu-es/strings.json index 72aab672..01084136 100644 --- a/translations/eu-es/strings.json +++ b/translations/eu-es/strings.json @@ -55,9 +55,20 @@ "content_key": "Mesedez, sartu satelitearekin erlazionatutako HiveMind entzule-gakoa:", "content_password": "Mesedez, idatzi satelitearekin erlazionatutako HiveMind entzulearen pasahitza:" }, + "speech.sh": { + "title": "Open Voice OS instalazioa - Ahotsa", + "content": "Open Voice OS-k esaten duzuna testu bihurtzen du (ahots-ezagutza) eta bere erantzunak ozen irakurtzen ditu (ahots-sintesia). Makina honek bi lanak berak egiteko adina indar du.\n\n - local: dena makina honetan exekutatzen da. Zure ahotsa hemen geratzen da, eta ezer ez dago Interneten edo zerbitzarien lan-kargaren mende. Instalatzaileak zure hizkuntzako ahots-ereduak deskargatzen ditu, ehunka megabytetik gigabyte gutxi batzuetara, eta ezagutzak prozesadorea lanpetuta mantentzen du hitz egiten duzun bitartean.\n - public: Open Voice OS-en zerbitzari publikoek egiten dute lana. Ez dago ezer deskargatu beharrik, baina eskaera bakoitza Internet bidez komunitate osoak partekatzen dituen zerbitzarietara doa. Horrek sareko atzerapena gehitzen du, eta motelago doa jende askok aldi berean erabiltzen dituenean. Zure grabazioak eta erantzunak zerbitzari horietan prozesatzen dira.\n\nEzagutza lokalak huts egiten badu edo ezer entzuten ez badu, grabazio hori zerbitzari publikoetara bidaltzen da.\n\nAukeratu non prozesatzen den ahotsa:", + "local_description": "Prozesatu ahotsa makina honetan", + "public_description": "Erabili Open Voice OS-en zerbitzari publikoak" + }, "summary.sh": { "title": "Ireki Voice OS instalazioa - Laburpena", - "content": "Ia amaitu duzu, hona hemen Open Voice OS instalatzeko egin dituzun aukeren laburpena:\n\n - Metodoa: ${METHOD:-}\n - Bertsioa: ${CHANNEL:-}\n - Profila: ${PROFILE:-}\n - Trebetasunak: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Trebetasun gehigarriak: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Afinazioa: ${TUNING_SUMMARY_STATE:-}\n\nOpen Voice OS instalazio-prozesuan egindako aukerak arretaz aztertu dira gure sistema zure behar eta lehentasun berezietara egokitzeko.\n\nLaburpen hau zuzena iruditzen zaizu? Hala ez bada, hautatu ${BACK_BUTTON:-} atzera egiteko eta aldaketak egiteko." + "content": "Ia amaitu duzu, hona hemen Open Voice OS instalatzeko egin dituzun aukeren laburpena:\n\n - Metodoa: ${METHOD:-}\n - Bertsioa: ${CHANNEL:-}\n - Profila: ${PROFILE:-}\n - Trebetasunak: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Trebetasun gehigarriak: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Ahotsa: ${SPEECH_SUMMARY_STATE:-}\n - Afinazioa: ${TUNING_SUMMARY_STATE:-}\n\nOpen Voice OS instalazio-prozesuan egindako aukerak arretaz aztertu dira gure sistema zure behar eta lehentasun berezietara egokitzeko.\n\nLaburpen hau zuzena iruditzen zaizu? Hala ez bada, hautatu ${BACK_BUTTON:-} atzera egiteko eta aldaketak egiteko.", + "speech_local": "makina honetan", + "speech_public": "zerbitzari publikoak", + "speech_public_hardware": "zerbitzari publikoak (lokala: 8 GB-ko Pi 5 edo hobea)", + "speech_public_setup": "zerbitzari publikoak (lokala: alpha eta virtualenv)", + "speech_unused": "profil honek ez du erabiltzen" }, "telemetry.sh": { "title": "Ireki Voice OS instalazioa - Telemetria", diff --git a/translations/fr-fr/strings.json b/translations/fr-fr/strings.json index 2dce6d1e..adab6472 100644 --- a/translations/fr-fr/strings.json +++ b/translations/fr-fr/strings.json @@ -56,9 +56,20 @@ "content_key": "Veuillez saisir la clé d'HiveMind Listener liée au satellite:", "content_password": "Veuillez saisir le mot de passe d'HiveMind Listener lié au satellite:" }, + "speech.sh": { + "title": "Open Voice OS Installation - Traitement de la parole", + "content": "Open Voice OS transforme ce que vous dites en texte (reconnaissance vocale) et lit ses réponses à voix haute (synthèse vocale). Cette machine est assez puissante pour faire les deux elle-même.\n\n- local : tout fonctionne sur cette machine. Votre voix reste ici, et rien ne dépend d'Internet ni de la charge des serveurs. L'installateur télécharge les modèles vocaux de votre langue, de quelques centaines de mégaoctets à quelques gigaoctets, et la reconnaissance occupe le processeur pendant que vous parlez.\n- public : les serveurs publics d'Open Voice OS font le travail. Rien à télécharger, mais chaque requête passe par Internet vers des serveurs partagés par toute la communauté, ce qui ajoute du délai réseau et ralentit quand beaucoup de monde les utilise en même temps. Vos enregistrements et les réponses sont traités sur ces serveurs.\n\nSi la reconnaissance locale échoue ou n'entend rien, cet enregistrement-là est envoyé aux serveurs publics à la place.\n\nVeuillez sélectionner où la parole est traitée :", + "local_description": "Traiter la parole sur cette machine", + "public_description": "Utiliser les serveurs publics d'Open Voice OS" + }, "summary.sh": { "title": "Open Voice OS Installation - Résumé", - "content": "Vous y êtes presque ! Voici un résumé des choix que vous avez effectués pour l'installation d'Open Voice OS :\n\n - Méthode d'installation : ${METHOD:-}\n - Canal de déploiement : ${CHANNEL:-}\n - Profil d'installation : ${PROFILE:-}\n - Compétences par défaut : ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Compétences supplémentaires : ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant : ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM : ${LLM_SUMMARY_STATE:-}\n - Réglages Raspberry Pi : ${TUNING_SUMMARY_STATE:-}\n\nLes choix effectués lors du processus d'installation d'Open Voice OS ont été soigneusement étudiés pour adapter notre système à vos besoins et préférences.\n\nEst-ce que tout cela vous semble correct ? Sinon, sélectionnez ${BACK_BUTTON:-} pour revenir en arrière et modifier vos choix." + "content": "Vous y êtes presque ! Voici un résumé des choix que vous avez effectués pour l'installation d'Open Voice OS :\n\n - Méthode d'installation : ${METHOD:-}\n - Canal de déploiement : ${CHANNEL:-}\n - Profil d'installation : ${PROFILE:-}\n - Compétences par défaut : ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Compétences supplémentaires : ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant : ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM : ${LLM_SUMMARY_STATE:-}\n - Traitement de la parole : ${SPEECH_SUMMARY_STATE:-}\n - Réglages Raspberry Pi : ${TUNING_SUMMARY_STATE:-}\n\nLes choix effectués lors du processus d'installation d'Open Voice OS ont été soigneusement étudiés pour adapter notre système à vos besoins et préférences.\n\nEst-ce que tout cela vous semble correct ? Sinon, sélectionnez ${BACK_BUTTON:-} pour revenir en arrière et modifier vos choix.", + "speech_local": "sur cette machine", + "speech_public": "serveurs publics", + "speech_public_hardware": "serveurs publics (local : Pi 5 8 Go minimum)", + "speech_public_setup": "serveurs publics (local : alpha et virtualenv)", + "speech_unused": "non utilisé par ce profil" }, "telemetry.sh": { "title": "Open Voice OS Installation - Télémétrie", diff --git a/translations/gl-es/strings.json b/translations/gl-es/strings.json index 299440b0..88221344 100644 --- a/translations/gl-es/strings.json +++ b/translations/gl-es/strings.json @@ -56,9 +56,20 @@ "content_key": "Introduce a clave do HiveMind Listener asociada ao satélite:", "content_password": "Introduce o contrasinal do HiveMind Listener asociado ao satélite:" }, + "speech.sh": { + "title": "Instalación de Open Voice OS - Voz", + "content": "Open Voice OS converte o que dis en texto (recoñecemento de voz) e le as súas respostas en voz alta (síntese de voz). Este equipo é abondo potente para facer as dúas cousas el mesmo.\n\n - local: todo se executa neste equipo. A túa voz queda aquí e nada depende de internet nin de canto de ocupados estean os servidores. O instalador descarga os modelos de voz do teu idioma, desde uns centos de megabytes ata uns poucos gigabytes, e o recoñecemento mantén ocupado o procesador mentres falas.\n - public: os servidores públicos de Open Voice OS fan o traballo. Non hai nada que descargar, pero cada petición viaxa por internet ata servidores que comparte toda a comunidade, o que engade atraso de rede e vólvese máis lento cando moita xente os usa á vez. As túas gravacións e as respostas procésanse neses servidores.\n\nSe o recoñecemento local falla ou non oe nada, esa gravación envíase aos servidores públicos.\n\nSelecciona onde se procesa a voz:", + "local_description": "Procesar a voz neste equipo", + "public_description": "Usar os servidores públicos de Open Voice OS" + }, "summary.sh": { "title": "Instalación de Open Voice OS - Recapitulación", - "content": "Estás a piques de rematar. Aquí tes un resumo das opcións que escolliches para instalar Open Voice OS:\n\n - Método: ${METHOD:-}\n - Versión: ${CHANNEL:-}\n - Perfil: ${PROFILE:-}\n - Habilidades: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Habilidades adicionais: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Axustes: ${TUNING_SUMMARY_STATE:-}\n\nAs decisións tomadas durante a instalación de Open Voice OS foron coidadosamente pensadas para adaptar o noso sistema ás túas necesidades e preferencias.\n\nÉ correcto este resumo? Se non, selecciona ${BACK_BUTTON:-} para volver atrás e facer cambios." + "content": "Estás a piques de rematar. Aquí tes un resumo das opcións que escolliches para instalar Open Voice OS:\n\n - Método: ${METHOD:-}\n - Versión: ${CHANNEL:-}\n - Perfil: ${PROFILE:-}\n - Habilidades: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Habilidades adicionais: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Voz: ${SPEECH_SUMMARY_STATE:-}\n - Axustes: ${TUNING_SUMMARY_STATE:-}\n\nAs decisións tomadas durante a instalación de Open Voice OS foron coidadosamente pensadas para adaptar o noso sistema ás túas necesidades e preferencias.\n\nÉ correcto este resumo? Se non, selecciona ${BACK_BUTTON:-} para volver atrás e facer cambios.", + "speech_local": "neste equipo", + "speech_public": "servidores públicos", + "speech_public_hardware": "servidores públicos (local: Pi 5 de 8 GB ou mellor)", + "speech_public_setup": "servidores públicos (local: alpha e virtualenv)", + "speech_unused": "este perfil non a usa" }, "telemetry.sh": { "title": "Instalación de Open Voice OS - Telemetría", diff --git a/translations/hi-in/strings.json b/translations/hi-in/strings.json index 74c8f951..6650ccb8 100644 --- a/translations/hi-in/strings.json +++ b/translations/hi-in/strings.json @@ -55,9 +55,20 @@ "content_key": "हाइवमाइंड सुनने वाले उपग्रह से संबंधित कुंजी दर्ज करें:", "content_password": "हाइवमाइंड सुनने वाले उपग्रह से संबंधित पासवर्ड दर्ज करें:" }, + "speech.sh": { + "title": "Open Voice OS Installation - आवाज़", + "content": "Open Voice OS आपकी कही बात को टेक्स्ट में बदलता है (वाक् पहचान) और अपने जवाब बोलकर सुनाता है (वाक् संश्लेषण)। यह मशीन ये दोनों काम ख़ुद करने के लिए काफ़ी शक्तिशाली है।\n\n - local: सब कुछ इसी मशीन पर चलता है। आपकी आवाज़ यहीं रहती है, और कुछ भी इंटरनेट या सर्वरों की व्यस्तता पर निर्भर नहीं करता। इंस्टॉलर आपकी भाषा के वाक् मॉडल डाउनलोड करता है, जो कुछ सौ मेगाबाइट से लेकर कुछ गीगाबाइट तक के होते हैं, और आपके बोलते समय पहचान प्रोसेसर को व्यस्त रखती है।\n - public: Open Voice OS के सार्वजनिक सर्वर यह काम करते हैं। कुछ डाउनलोड नहीं करना पड़ता, लेकिन हर अनुरोध इंटरनेट से होकर उन सर्वरों तक जाता है जिन्हें पूरा समुदाय साझा करता है। इससे नेटवर्क की देरी जुड़ती है, और जब बहुत से लोग एक साथ इनका उपयोग करते हैं तो जवाब धीमे हो जाते हैं। आपकी रिकॉर्डिंग और जवाब इन्हीं सर्वरों पर प्रोसेस होते हैं।\n\nअगर लोकल पहचान विफल हो जाए या कुछ न सुन पाए, तो वह रिकॉर्डिंग सार्वजनिक सर्वरों पर भेज दी जाती है।\n\nकृपया चुनें कि आवाज़ कहाँ प्रोसेस हो:", + "local_description": "इसी मशीन पर आवाज़ प्रोसेस करें", + "public_description": "Open Voice OS के सार्वजनिक सर्वर इस्तेमाल करें" + }, "summary.sh": { "title": "Open Voice OS Installation - सारांश", - "content": "आपका काम लगभग पूरा हो चुका है, ओपन वॉयस ओएस (Open Voice OS) स्थापित करने के लिए आपके द्वारा चुने गए विकल्पों का सारांश यहां दिया गया है:\n - विधि: ${METHOD:-}\n - संस्करण: ${CHANNEL:-}\n - प्रोफाइल: ${PROFILE:-}\n - कौशल: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - अतिरिक्त कौशल: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - ट्यूनिंग: ${TUNING_SUMMARY_STATE:-}\n\nओपन वॉयस ओएस (Open Voice OS) इंस्टॉलेशन प्रक्रिया के दौरान चुने गए विकल्पों पर हमारे सिस्टम (System) को आपकी विशिष्ट आवश्यकताओं और प्राथमिकताओं के अनुरूप बनाने के लिए सावधानीपूर्वक विचार किया गया है।\nक्या आपको यह सही लगता है? यदि नहीं, तो वापस जाने और बदलाव करने के लिए ${BACK_BUTTON:-} चुनें।" + "content": "आपका काम लगभग पूरा हो चुका है, ओपन वॉयस ओएस (Open Voice OS) स्थापित करने के लिए आपके द्वारा चुने गए विकल्पों का सारांश यहां दिया गया है:\n - विधि: ${METHOD:-}\n - संस्करण: ${CHANNEL:-}\n - प्रोफाइल: ${PROFILE:-}\n - कौशल: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - अतिरिक्त कौशल: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - आवाज़: ${SPEECH_SUMMARY_STATE:-}\n - ट्यूनिंग: ${TUNING_SUMMARY_STATE:-}\n\nओपन वॉयस ओएस (Open Voice OS) इंस्टॉलेशन प्रक्रिया के दौरान चुने गए विकल्पों पर हमारे सिस्टम (System) को आपकी विशिष्ट आवश्यकताओं और प्राथमिकताओं के अनुरूप बनाने के लिए सावधानीपूर्वक विचार किया गया है।\nक्या आपको यह सही लगता है? यदि नहीं, तो वापस जाने और बदलाव करने के लिए ${BACK_BUTTON:-} चुनें।", + "speech_local": "इसी मशीन पर", + "speech_public": "सार्वजनिक सर्वर", + "speech_public_hardware": "सार्वजनिक सर्वर (लोकल: 8 GB वाला Pi 5 या बेहतर)", + "speech_public_setup": "सार्वजनिक सर्वर (लोकल: alpha और virtualenv)", + "speech_unused": "यह प्रोफ़ाइल इसका उपयोग नहीं करती" }, "telemetry.sh": { "title": "Open Voice OS Installation - टेलीमेटरी", diff --git a/translations/it-it/strings.json b/translations/it-it/strings.json index 5b03927d..4c04088e 100644 --- a/translations/it-it/strings.json +++ b/translations/it-it/strings.json @@ -56,9 +56,20 @@ "content_key": "Immettere la chiave di ascolto HiveMind del satellite:", "content_password": "Inserire la password dell'ascoltatore di HiveMind del satellite:" }, + "speech.sh": { + "title": "Installazione di Open Voice OS - Voce", + "content": "Open Voice OS trasforma ciò che dici in testo (riconoscimento vocale) e legge le risposte ad alta voce (sintesi vocale). Questa macchina è abbastanza potente da fare entrambe le cose da sola.\n\n - local: tutto gira su questa macchina. La tua voce resta qui e nulla dipende da internet o da quanto sono occupati i server. L'installer scarica i modelli vocali per la tua lingua, da qualche centinaio di megabyte a qualche gigabyte, e il riconoscimento tiene occupato il processore mentre parli.\n - public: il lavoro lo fanno i server pubblici di Open Voice OS. Niente da scaricare, ma ogni richiesta viaggia su internet verso server condivisi da tutta la comunità: questo aggiunge ritardo di rete e rallenta quando molte persone li usano contemporaneamente. Le tue registrazioni e le risposte vengono elaborate su quei server.\n\nSe il riconoscimento locale fallisce o non sente nulla, quella registrazione viene inviata ai server pubblici.\n\nSeleziona dove elaborare la voce:", + "local_description": "Elaborare la voce su questa macchina", + "public_description": "Usare i server pubblici di Open Voice OS" + }, "summary.sh": { "title": "Installazione di Open Voice OS - Riassunto", - "content": "Abbiamo quasi finito. Qui c'è un riassunto delle opzioni scelte per installare Open Voice OS:\n\n - Distribuzione: ${METHOD:-}\n - Versione: ${CHANNEL:-}\n - Profilo: ${PROFILE:-}\n - Competenze: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Competenze extra: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Ottimizzazione: ${TUNING_SUMMARY_STATE:-}\n\nLe decisioni prese durante il processo di installazione di Open Voice OS sono state attentamente valutate per personalizzare il nostro sistema in base alle tue esigenze e preferenze individuali.\n\nLe impostazioni sono corrette? In caso contrario, seleziona ${BACK_BUTTON:-} per tornare indietro e apportare modifiche." + "content": "Abbiamo quasi finito. Qui c'è un riassunto delle opzioni scelte per installare Open Voice OS:\n\n - Distribuzione: ${METHOD:-}\n - Versione: ${CHANNEL:-}\n - Profilo: ${PROFILE:-}\n - Competenze: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Competenze extra: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Voce: ${SPEECH_SUMMARY_STATE:-}\n - Ottimizzazione: ${TUNING_SUMMARY_STATE:-}\n\nLe decisioni prese durante il processo di installazione di Open Voice OS sono state attentamente valutate per personalizzare il nostro sistema in base alle tue esigenze e preferenze individuali.\n\nLe impostazioni sono corrette? In caso contrario, seleziona ${BACK_BUTTON:-} per tornare indietro e apportare modifiche.", + "speech_local": "su questa macchina", + "speech_public": "server pubblici", + "speech_public_hardware": "server pubblici (locale: Pi 5 con 8 GB o superiore)", + "speech_public_setup": "server pubblici (locale: alpha e virtualenv)", + "speech_unused": "non usata da questo profilo" }, "telemetry.sh": { "title": "Installazione di Open Voice OS - Telemetria", diff --git a/translations/kab-dz/strings.json b/translations/kab-dz/strings.json index c35184bb..50edfb93 100644 --- a/translations/kab-dz/strings.json +++ b/translations/kab-dz/strings.json @@ -54,14 +54,25 @@ "content_key": "Ttxil, sekcem-d tsarut n umesseflid n HiveMind yeqqnen ɣer ugensa-nni:", "content_password": "Ttxil, sekcem-d awal n uɛeddi n umesseflid HiveMind yeqqnen ɣer ugensa-nni:" }, + "speech.sh": { + "title": "Asbeddi n Open Voice OS - Taɣect", + "content": "Open Voice OS yettbeddil ayen i d-tenniḍ ɣer uḍris (aɛqal n taɣect) yerna yeqqar-d tiririyin-is s taɣect (asuddes n taɣect). Tamacint-a tezmer ad txeddem snat-a s yiman-is.\n\n - local: kullec ad iteddu ɣef tmacint-a. Taɣect-ik ad teqqim da, ulac ayen icudden ɣer Internet neɣ ɣer yiqeddacen. Amesbeddi ad d-yessader timudmin n taɣect n tutlayt-ik, seg kra n yimiḍ n MB alamma d kra n GB, yerna aɛqal ad yesseqdec amesbadu mi ara tettmeslayeḍ.\n - public: d iqeddacen izayazen n Open Voice OS ara ixedmen axeddim. Ulac ayen ara d-tessadreḍ, maca yal asuter ad yeddu s Internet ɣer yiqeddacen i tebḍa merra temɣiwant: aya yettarra-d aɛettel, yerna ad ẓẓayen mi ara ten-sqedcen aṭas n medden ɣef tikkelt. Iseklasen-ik d tririyin ad ttwasenfaren ɣef yiqeddacen-a.\n\nMa yecceḍ uɛqal adigan neɣ ur yesla i kra, asekles-nni ad yettwazen ɣer yiqeddacen izayazen.\n\nTtxil-k fren anida ara tettwasenfar taɣect:", + "local_description": "Taɣect ɣef tmacint-a", + "public_description": "Iqeddacen izayazen n Open Voice OS" + }, "summary.sh": { "title": "Asbeddi n Open Voice OS - Agzul", - "content": "Qrib ad tekfuḍ, ha-t-an ugzul n textiṛiyin i tgiḍ akken ad tesbeddeḍ anagraw n Open Voice OS\n\n - Tarrayt: ${METHOD:-}\n - Lqem: ${CHANNEL:-}\n - Amaɣnu: ${PROFILE:-}\n - Tussniwin: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Tussniwin timernanin: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Tamellit: ${TUNING_SUMMARY_STATE:-}\n\nIfranen i d-yettwaxedmen deg ukala n usbeddi n Open Voice OS ttwafernen s ttawil akked ufeṣṣel i wakken ad yeddu unagraw-nneɣ akken i teḥwaǧeḍ.\n\nYettban-ak-d iṣeḥḥa ugzul-a? Ma ulac, fren ${BACK_BUTTON:-} akken ad tuɣaleḍ ɣer deffir ad tbeddleḍ.", + "content": "Qrib ad tekfuḍ, ha-t-an ugzul n textiṛiyin i tgiḍ akken ad tesbeddeḍ anagraw n Open Voice OS\n\n - Tarrayt: ${METHOD:-}\n - Lqem: ${CHANNEL:-}\n - Amaɣnu: ${PROFILE:-}\n - Tussniwin: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Tussniwin timernanin: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Taɣect: ${SPEECH_SUMMARY_STATE:-}\n - Tamellit: ${TUNING_SUMMARY_STATE:-}\n\nIfranen i d-yettwaxedmen deg ukala n usbeddi n Open Voice OS ttwafernen s ttawil akked ufeṣṣel i wakken ad yeddu unagraw-nneɣ akken i teḥwaǧeḍ.\n\nYettban-ak-d iṣeḥḥa ugzul-a? Ma ulac, fren ${BACK_BUTTON:-} akken ad tuɣaleḍ ɣer deffir ad tbeddleḍ.", "state_enabled": "d urmid", "state_disabled": "d arurmid", "state_unsupported_profile": "yettawfran (ur yettwasefrek ara deg umaɣnu-a)", "state_missing_url": "yettwafran (txuṣṣ URL; as yettwazgal)", - "state_missing_configuration": "yettwafran (txuṣṣ tawila; as yettwazgal)" + "state_missing_configuration": "yettwafran (txuṣṣ tawila; as yettwazgal)", + "speech_local": "ɣef tmacint-a", + "speech_public": "iqeddacen izayazen", + "speech_public_hardware": "iqeddacen izayazen (adigan: Pi 5 s 8 GB neɣ ugar)", + "speech_public_setup": "iqeddacen izayazen (adigan: alpha d virtualenv)", + "speech_unused": "ur yettwaseqdac ara deg umaɣnu-a" }, "telemetry.sh": { "title": "Asbeddi n Open Voice OS - Aktili", diff --git a/translations/nl-nl/strings.json b/translations/nl-nl/strings.json index 9c12a84f..b3702f94 100644 --- a/translations/nl-nl/strings.json +++ b/translations/nl-nl/strings.json @@ -56,9 +56,20 @@ "content_key": "Voer de HiveMind listener sleutel in, behorende bij de satelliet:", "content_password": "Voer het HiveMind listener wachtwoord in, behorende bij de satelliet:" }, + "speech.sh": { + "title": "Open Voice OS Installatie - Spraak", + "content": "Open Voice OS zet wat je zegt om in tekst (spraakherkenning) en leest zijn antwoorden voor (spraaksynthese). Deze machine is krachtig genoeg om beide zelf te doen.\n\n- local: alles draait op deze machine. Je stem blijft hier, en niets hangt af van internet of van hoe druk de servers zijn. Het installatieprogramma downloadt de spraakmodellen voor je taal, van een paar honderd megabyte tot een paar gigabyte, en de herkenning houdt de processor bezig terwijl je praat.\n- public: de openbare servers van Open Voice OS doen het werk. Je hoeft niets te downloaden, maar elk verzoek gaat via internet naar servers die de hele community deelt. Dat voegt netwerkvertraging toe en wordt trager wanneer veel mensen ze tegelijk gebruiken. Je opnames en de antwoorden worden op die servers verwerkt.\n\nAls de lokale herkenning mislukt of niets hoort, wordt die ene opname naar de openbare servers gestuurd.\n\nSelecteer waar spraak wordt verwerkt:", + "local_description": "Spraak op deze machine verwerken", + "public_description": "De openbare Open Voice OS-servers gebruiken" + }, "summary.sh": { "title": "OpenVoice OS Installatie - Samenvatting", - "content": "Bijna klaar. Een korte samenvatting:\n\n - Omgeving: ${METHOD:-}\n - Versie: ${CHANNEL:-}\n - Profiel: ${PROFILE:-}\n - Skills: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Extra skills: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Tuning: ${TUNING_SUMMARY_STATE:-}\n\nDe beslissingen die zijn genomen tijdens het installatieproces van OpenVoice OS zijn zorgvuldig gemaakt om ons systeem aan te passen aan je individuele behoeften en voorkeuren.\n\nZijn de instellingen zo in orde? Zo niet, kies ${BACK_BUTTON:-} om terug te gaan en wijzigingen te maken." + "content": "Bijna klaar. Een korte samenvatting:\n\n - Omgeving: ${METHOD:-}\n - Versie: ${CHANNEL:-}\n - Profiel: ${PROFILE:-}\n - Skills: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Extra skills: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Spraak: ${SPEECH_SUMMARY_STATE:-}\n - Tuning: ${TUNING_SUMMARY_STATE:-}\n\nDe beslissingen die zijn genomen tijdens het installatieproces van OpenVoice OS zijn zorgvuldig gemaakt om ons systeem aan te passen aan je individuele behoeften en voorkeuren.\n\nZijn de instellingen zo in orde? Zo niet, kies ${BACK_BUTTON:-} om terug te gaan en wijzigingen te maken.", + "speech_local": "op deze machine", + "speech_public": "openbare servers", + "speech_public_hardware": "openbare servers (lokaal: Pi 5 met 8 GB of beter)", + "speech_public_setup": "openbare servers (lokaal: alpha en virtualenv)", + "speech_unused": "niet gebruikt door dit profiel" }, "telemetry.sh": { "title": "OpenVoice OS Installatie - Telemetrie", diff --git a/translations/pl-pl/strings.json b/translations/pl-pl/strings.json index 298d74dc..27d0c43f 100644 --- a/translations/pl-pl/strings.json +++ b/translations/pl-pl/strings.json @@ -56,9 +56,20 @@ "content_key": "Proszę wprowadzić klucz nasłuchu HiveMind związany z satelitą:", "content_password": "Proszę wprowadzić hasło nasłuchujące HiveMind powiązane z satelitą:" }, + "speech.sh": { + "title": "Instalacja Open Voice OS - Mowa", + "content": "Open Voice OS zamienia to, co mówisz, na tekst (rozpoznawanie mowy) i czyta swoje odpowiedzi na głos (synteza mowy). Ten komputer jest na tyle wydajny, żeby robić jedno i drugie samodzielnie.\n\n- local: wszystko działa na tym komputerze. Twój głos zostaje tutaj i nic nie zależy od internetu ani od obciążenia serwerów. Instalator pobiera modele mowy dla Twojego języka, od kilkuset megabajtów do kilku gigabajtów, a rozpoznawanie obciąża procesor, gdy mówisz.\n- public: pracę wykonują publiczne serwery Open Voice OS. Nic nie trzeba pobierać, ale każde żądanie wędruje przez internet do serwerów współdzielonych przez całą społeczność, co dodaje opóźnienie sieciowe i spowalnia działanie, gdy korzysta z nich wiele osób naraz. Twoje nagrania i odpowiedzi są przetwarzane na tych serwerach.\n\nJeśli lokalne rozpoznawanie zawiedzie lub nic nie usłyszy, to nagranie zostanie wysłane do serwerów publicznych.\n\nWybierz, gdzie ma być przetwarzana mowa:", + "local_description": "Przetwarzaj mowę na tym komputerze", + "public_description": "Używaj publicznych serwerów Open Voice OS" + }, "summary.sh": { "title": "Instalacja Open Voice OS – Podsumowanie", - "content": "Już prawie skończyłeś, oto podsumowanie wyborów dokonanych podczas instalacji Open Voice OS:\n\n- Metoda: ${METHOD:-}\n- Wersja: ${CHANNEL:-}\n- Profil: ${PROFILE:-}\n- Umiejętności: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n- Dodatkowe umiejętności: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n- Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n- LLM: ${LLM_SUMMARY_STATE:-}\n- Strojenie: ${TUNING_SUMMARY_STATE:-}\n\nWybory dokonane podczas instalacji Open Voice OS zostały starannie rozważone, aby dostosować nasz system do Twoich unikalnych potrzeb i preferencji.\n\nCzy to podsumowanie wydaje Ci się poprawne? Jeśli nie, wybierz ${BACK_BUTTON:-}, aby wrócić i wprowadzić zmiany." + "content": "Już prawie skończyłeś, oto podsumowanie wyborów dokonanych podczas instalacji Open Voice OS:\n\n- Metoda: ${METHOD:-}\n- Wersja: ${CHANNEL:-}\n- Profil: ${PROFILE:-}\n- Umiejętności: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n- Dodatkowe umiejętności: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n- Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n- LLM: ${LLM_SUMMARY_STATE:-}\n- Mowa: ${SPEECH_SUMMARY_STATE:-}\n- Strojenie: ${TUNING_SUMMARY_STATE:-}\n\nWybory dokonane podczas instalacji Open Voice OS zostały starannie rozważone, aby dostosować nasz system do Twoich unikalnych potrzeb i preferencji.\n\nCzy to podsumowanie wydaje Ci się poprawne? Jeśli nie, wybierz ${BACK_BUTTON:-}, aby wrócić i wprowadzić zmiany.", + "speech_local": "na tym komputerze", + "speech_public": "serwery publiczne", + "speech_public_hardware": "serwery publiczne (lokalnie: Pi 5 z 8 GB lub lepszy)", + "speech_public_setup": "serwery publiczne (lokalnie: alpha i virtualenv)", + "speech_unused": "nieużywana w tym profilu" }, "telemetry.sh": { "title": "Instalacja Open Voice OS - Telemetria", diff --git a/translations/pt-pt/strings.json b/translations/pt-pt/strings.json index ed3bbf08..186289cc 100644 --- a/translations/pt-pt/strings.json +++ b/translations/pt-pt/strings.json @@ -56,9 +56,20 @@ "content_key": "Introduza a chave de acesso do servidor HiveMind para o satélite:", "content_password": "Introduza a palavra-passe do servidor HiveMind para o satélite:" }, + "speech.sh": { + "title": "Open Voice OS Instalação - Voz", + "content": "O Open Voice OS converte o que diz em texto (reconhecimento de voz) e lê as respostas em voz alta (síntese de voz). Esta máquina é suficientemente potente para fazer as duas coisas sozinha.\n\n - local: tudo corre nesta máquina. A sua voz fica aqui e nada depende da internet nem da ocupação dos servidores. O instalador descarrega os modelos de voz do seu idioma, de algumas centenas de megabytes a alguns gigabytes, e o reconhecimento mantém o processador ocupado enquanto fala.\n - public: os servidores públicos do Open Voice OS fazem o trabalho. Não há nada para descarregar, mas cada pedido viaja pela internet até servidores partilhados por toda a comunidade, o que acrescenta atraso de rede e fica mais lento quando muitas pessoas os usam ao mesmo tempo. As suas gravações e as respostas são processadas nesses servidores.\n\nSe o reconhecimento local falhar ou não ouvir nada, essa gravação é enviada para os servidores públicos.\n\nSeleccione onde a voz é processada:", + "local_description": "Processar a voz nesta máquina", + "public_description": "Usar os servidores públicos do Open Voice OS" + }, "summary.sh": { "title": "Open Voice OS Instalação - Resumo", - "content": "Quase a terminar. Um breve resumo:\n\n - Implementação: ${METHOD:-}\n - Versão: ${CHANNEL:-}\n - Perfil: ${PROFILE:-}\n - Skills: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Skills extra: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Otimização: ${TUNING_SUMMARY_STATE:-}\n\nAs decisões tomadas durante o processo de instalação do Open Voice OS foram cuidadosamente consideradas para personalizar o nosso sistema de acordo com as suas necessidades e preferências individuais.\n\nAs definições estão correctas? Se não, seleccione ${BACK_BUTTON:-} para voltar atrás e fazer alterações." + "content": "Quase a terminar. Um breve resumo:\n\n - Implementação: ${METHOD:-}\n - Versão: ${CHANNEL:-}\n - Perfil: ${PROFILE:-}\n - Skills: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Skills extra: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Voz: ${SPEECH_SUMMARY_STATE:-}\n - Otimização: ${TUNING_SUMMARY_STATE:-}\n\nAs decisões tomadas durante o processo de instalação do Open Voice OS foram cuidadosamente consideradas para personalizar o nosso sistema de acordo com as suas necessidades e preferências individuais.\n\nAs definições estão correctas? Se não, seleccione ${BACK_BUTTON:-} para voltar atrás e fazer alterações.", + "speech_local": "nesta máquina", + "speech_public": "servidores públicos", + "speech_public_hardware": "servidores públicos (local: Pi 5 com 8 GB ou melhor)", + "speech_public_setup": "servidores públicos (local: alpha e virtualenv)", + "speech_unused": "não utilizado por este perfil" }, "telemetry.sh": { "title": "Open Voice OS Instalação - Telemetria", diff --git a/tui/locales/ca-es/speech.sh b/tui/locales/ca-es/speech.sh new file mode 100644 index 00000000..546eeda7 --- /dev/null +++ b/tui/locales/ca-es/speech.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +CONTENT=" +L'Open Voice OS converteix el que dius en text (reconeixement de veu) i llegeix les respostes en veu alta (síntesi de veu). Aquest equip és prou potent per fer totes dues coses ell mateix. + +- local: tot s'executa en aquest equip. La teva veu es queda aquí i res no depèn d'internet ni de com d'ocupats estiguin els servidors. L'instal·lador baixa els models de veu del teu idioma, des d'uns quants centenars de megabytes fins a uns quants gigabytes, i el reconeixement manté ocupat el processador mentre parles. +- public: els servidors públics de l'Open Voice OS fan la feina. No cal baixar res, però cada petició viatja per internet fins a servidors que comparteix tota la comunitat, cosa que hi afegeix retard de xarxa i que s'alenteix quan molta gent els fa servir alhora. Les teves gravacions i les respostes es processen en aquests servidors. + +Si el reconeixement local falla o no sent res, aquella gravació s'envia als servidors públics. + +Selecciona on es processa la veu, si us plau: +" +TITLE="Instal·lació de l'Open Voice OS - Veu" +LOCAL_DESCRIPTION="Processar la veu en aquest equip" +PUBLIC_DESCRIPTION="Servidors públics de l'Open Voice OS" + +export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/ca-es/summary.sh b/tui/locales/ca-es/summary.sh index e7f0c5b2..fecec480 100644 --- a/tui/locales/ca-es/summary.sh +++ b/tui/locales/ca-es/summary.sh @@ -9,6 +9,7 @@ Gairebé heu acabat, aquí teniu un resum de les opcions que heu triat en instal - Habilitats extra: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-} - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-} - LLM: ${LLM_SUMMARY_STATE:-} + - Veu: ${SPEECH_SUMMARY_STATE:-} - Afinació: ${TUNING_SUMMARY_STATE:-} Les opcions seleccionades durant el procés d'instal·lació de l'Open Voice OS s'han considerat acuradament per a adaptar el nostre sistema a les vostres necessitats i preferències úniques. @@ -22,5 +23,10 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" +SUMMARY_SPEECH_LOCAL="en aquest equip" +SUMMARY_SPEECH_PUBLIC="servidors públics" +SUMMARY_SPEECH_PUBLIC_HARDWARE="servidors públics (local: Pi 5 de 8 GB o més)" +SUMMARY_SPEECH_PUBLIC_SETUP="servidors públics (local: alpha i virtualenv)" +SUMMARY_SPEECH_UNUSED="aquest perfil no la fa servir" -export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION +export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/da/speech.sh b/tui/locales/da/speech.sh new file mode 100644 index 00000000..12cf7ebc --- /dev/null +++ b/tui/locales/da/speech.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +CONTENT=" +Open Voice OS omsætter det, du siger, til tekst (talegenkendelse) og læser sine svar højt (talesyntese). Denne maskine er kraftig nok til selv at klare begge dele. + +- local: alt kører på denne maskine. Din stemme bliver her, og intet afhænger af internettet eller af, hvor travlt serverne har. Installationsprogrammet henter talemodellerne til dit sprog, fra et par hundrede megabyte til et par gigabyte, og genkendelsen holder processoren beskæftiget, mens du taler. +- public: de offentlige Open Voice OS-servere klarer arbejdet. Der skal ikke hentes noget, men hver forespørgsel går over internettet til servere, som hele fællesskabet deler. Det giver netværksforsinkelse og bliver langsommere, når mange bruger dem på samme tid. Dine optagelser og svarene behandles på de servere. + +Hvis den lokale genkendelse fejler eller ikke hører noget, sendes netop den optagelse til de offentlige servere i stedet. + +Vælg venligst, hvor tale skal behandles: +" +TITLE="Open Voice OS Installation - Tale" +LOCAL_DESCRIPTION="Behandl tale på denne maskine" +PUBLIC_DESCRIPTION="Brug de offentlige Open Voice OS-servere" + +export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/da/summary.sh b/tui/locales/da/summary.sh index 901bb1a6..4d762461 100644 --- a/tui/locales/da/summary.sh +++ b/tui/locales/da/summary.sh @@ -9,6 +9,7 @@ Du er næsten færdig, her er en oversigt over de valg, du har truffet for at in - Ekstra færdigheder: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-} - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-} - LLM: ${LLM_SUMMARY_STATE:-} + - Tale: ${SPEECH_SUMMARY_STATE:-} - Tuning: ${TUNING_SUMMARY_STATE:-} De valg, der blev truffet under installationen af ​​Open Voice OS, er blevet nøje overvejet for at skræddersy vores system til dine unikke behov og præferencer. @@ -22,5 +23,10 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" +SUMMARY_SPEECH_LOCAL="på denne maskine" +SUMMARY_SPEECH_PUBLIC="offentlige servere" +SUMMARY_SPEECH_PUBLIC_HARDWARE="offentlige servere (lokal: Pi 5 med 8 GB eller mere)" +SUMMARY_SPEECH_PUBLIC_SETUP="offentlige servere (lokal: alpha og virtualenv)" +SUMMARY_SPEECH_UNUSED="bruges ikke af denne profil" -export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION +export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/de-de/speech.sh b/tui/locales/de-de/speech.sh new file mode 100644 index 00000000..b0760714 --- /dev/null +++ b/tui/locales/de-de/speech.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +CONTENT=" +Open Voice OS wandelt Gesprochenes in Text um (Spracherkennung) und liest seine Antworten vor (Sprachsynthese). Dieser Rechner ist leistungsfähig genug, beides selbst zu erledigen. + +- local: Alles läuft auf diesem Rechner. Ihre Stimme bleibt hier, und nichts hängt vom Internet oder von der Auslastung der Server ab. Der Installer lädt die Sprachmodelle für Ihre Sprache herunter, von einigen hundert Megabyte bis zu ein paar Gigabyte, und die Erkennung lastet den Prozessor aus, während Sie sprechen. +- public: Die öffentlichen Open Voice OS-Server übernehmen die Arbeit. Es muss nichts heruntergeladen werden, aber jede Anfrage geht über das Internet an Server, die sich die ganze Community teilt. Das kostet Netzwerklaufzeit und wird langsamer, wenn viele sie gleichzeitig nutzen. Ihre Aufnahmen und die Antworten werden auf diesen Servern verarbeitet. + +Wenn die lokale Erkennung fehlschlägt oder nichts hört, wird genau diese Aufnahme stattdessen an die öffentlichen Server geschickt. + +Bitte wählen Sie, wo die Sprache verarbeitet wird: +" +TITLE="Open Voice OS-Installation - Sprachverarbeitung" +LOCAL_DESCRIPTION="Sprache auf diesem Rechner verarbeiten" +PUBLIC_DESCRIPTION="Öffentliche Open Voice OS-Server nutzen" + +export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/de-de/summary.sh b/tui/locales/de-de/summary.sh index 715ad0c9..ce6b71f4 100644 --- a/tui/locales/de-de/summary.sh +++ b/tui/locales/de-de/summary.sh @@ -9,6 +9,7 @@ Fast fertig. Eine kurze Zusammenfassung: - Extra-Skills: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-} - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-} - LLM: ${LLM_SUMMARY_STATE:-} + - Sprache: ${SPEECH_SUMMARY_STATE:-} - Tuning: ${TUNING_SUMMARY_STATE:-} Die Entscheidungen, die während des Installationsprozesses von Open Voice OS getroffen werden, wurden sorgfältig abgewogen, um unser System an Ihre individuellen Bedürfnisse und Vorlieben anzupassen. @@ -22,5 +23,10 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" +SUMMARY_SPEECH_LOCAL="auf diesem Rechner" +SUMMARY_SPEECH_PUBLIC="öffentliche Server" +SUMMARY_SPEECH_PUBLIC_HARDWARE="öffentliche Server (lokal: Pi 5 mit 8 GB oder besser)" +SUMMARY_SPEECH_PUBLIC_SETUP="öffentliche Server (lokal: alpha und virtualenv)" +SUMMARY_SPEECH_UNUSED="von diesem Profil nicht genutzt" -export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION +export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/en-us/speech.sh b/tui/locales/en-us/speech.sh new file mode 100644 index 00000000..84b33c84 --- /dev/null +++ b/tui/locales/en-us/speech.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +CONTENT=" +Open Voice OS turns what you say into text (speech recognition) and reads its answers out loud (speech synthesis). This machine is powerful enough to do both itself. + + - local: everything runs on this machine. Your voice stays here, and nothing depends on the internet or on how busy the servers are. The installer downloads the speech models for your language, from a few hundred megabytes to a few gigabytes, and recognition keeps the processor busy while you talk. + - public: the Open Voice OS public servers do the work. Nothing to download, but every request travels over the internet to servers the whole community shares, which adds network delay and slows down when many people use them at once. Your recordings and the answers are processed on those servers. + +When local recognition fails or hears nothing, that one recording is sent to the public servers instead. + +Please select where speech is processed: +" +TITLE="Open Voice OS Installation - Speech" +LOCAL_DESCRIPTION="Run speech on this machine" +PUBLIC_DESCRIPTION="Use the Open Voice OS public servers" + +export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/en-us/summary.sh b/tui/locales/en-us/summary.sh index b3d173b3..07c25363 100644 --- a/tui/locales/en-us/summary.sh +++ b/tui/locales/en-us/summary.sh @@ -9,6 +9,7 @@ You are almost done, here is a summary of choices you made to install Open Voice - Extra: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-} - HA: ${HOMEASSISTANT_SUMMARY_STATE:-} - LLM: ${LLM_SUMMARY_STATE:-} + - Speech: ${SPEECH_SUMMARY_STATE:-} - Tuning: ${TUNING_SUMMARY_STATE:-} The choices made during the Open Voice OS installation process have been carefully considered to tailor our system to your unique needs and preferences. @@ -22,5 +23,10 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" +SUMMARY_SPEECH_LOCAL="on this machine" +SUMMARY_SPEECH_PUBLIC="public servers" +SUMMARY_SPEECH_PUBLIC_HARDWARE="public servers (local needs a Pi 5 with 8 GB or better)" +SUMMARY_SPEECH_PUBLIC_SETUP="public servers (local needs alpha and virtualenv)" +SUMMARY_SPEECH_UNUSED="not used by this profile" -export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION +export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/es-es/speech.sh b/tui/locales/es-es/speech.sh new file mode 100644 index 00000000..a8f66e18 --- /dev/null +++ b/tui/locales/es-es/speech.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +CONTENT=" +Open Voice OS convierte lo que dice en texto (reconocimiento de voz) y lee sus respuestas en voz alta (síntesis de voz). Este equipo es lo bastante potente para hacer ambas cosas por sí mismo. + +- local: todo se ejecuta en este equipo. Su voz se queda aquí y nada depende de internet ni de lo ocupados que estén los servidores. El instalador descarga los modelos de voz de su idioma, desde unos cientos de megabytes hasta unos pocos gigabytes, y el reconocimiento mantiene ocupado el procesador mientras habla. +- public: los servidores públicos de Open Voice OS hacen el trabajo. No hay nada que descargar, pero cada petición viaja por internet hasta servidores que comparte toda la comunidad, lo que añade retardo de red y se vuelve más lento cuando muchas personas los usan a la vez. Sus grabaciones y las respuestas se procesan en esos servidores. + +Si el reconocimiento local falla o no oye nada, esa grabación se envía a los servidores públicos. + +Seleccione dónde se procesa la voz: +" +TITLE="Instalación de Open Voice OS - Voz" +LOCAL_DESCRIPTION="Procesar la voz en este equipo" +PUBLIC_DESCRIPTION="Usar los servidores públicos de Open Voice OS" + +export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/es-es/summary.sh b/tui/locales/es-es/summary.sh index 1d579545..c2e08d1e 100644 --- a/tui/locales/es-es/summary.sh +++ b/tui/locales/es-es/summary.sh @@ -9,6 +9,7 @@ CONTENT=" - Extras: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-} - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-} - LLM: ${LLM_SUMMARY_STATE:-} +- Voz: ${SPEECH_SUMMARY_STATE:-} - Ajustes: ${TUNING_SUMMARY_STATE:-} Las decisiones que has tomado durante el proceso de instalación de Open Voice OS han sido cuidadosamente consideradas para adaptar el sistema a tus necesidades y preferencias. @@ -22,5 +23,10 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" +SUMMARY_SPEECH_LOCAL="en este equipo" +SUMMARY_SPEECH_PUBLIC="servidores públicos" +SUMMARY_SPEECH_PUBLIC_HARDWARE="servidores públicos (local: Pi 5 con 8 GB o más)" +SUMMARY_SPEECH_PUBLIC_SETUP="servidores públicos (local: alpha y virtualenv)" +SUMMARY_SPEECH_UNUSED="este perfil no la usa" -export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION +export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/eu-es/speech.sh b/tui/locales/eu-es/speech.sh new file mode 100644 index 00000000..d6155819 --- /dev/null +++ b/tui/locales/eu-es/speech.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +CONTENT=" +Open Voice OS-k esaten duzuna testu bihurtzen du (ahots-ezagutza) eta bere erantzunak ozen irakurtzen ditu (ahots-sintesia). Makina honek bi lanak berak egiteko adina indar du. + + - local: dena makina honetan exekutatzen da. Zure ahotsa hemen geratzen da, eta ezer ez dago Interneten edo zerbitzarien lan-kargaren mende. Instalatzaileak zure hizkuntzako ahots-ereduak deskargatzen ditu, ehunka megabytetik gigabyte gutxi batzuetara, eta ezagutzak prozesadorea lanpetuta mantentzen du hitz egiten duzun bitartean. + - public: Open Voice OS-en zerbitzari publikoek egiten dute lana. Ez dago ezer deskargatu beharrik, baina eskaera bakoitza Internet bidez komunitate osoak partekatzen dituen zerbitzarietara doa. Horrek sareko atzerapena gehitzen du, eta motelago doa jende askok aldi berean erabiltzen dituenean. Zure grabazioak eta erantzunak zerbitzari horietan prozesatzen dira. + +Ezagutza lokalak huts egiten badu edo ezer entzuten ez badu, grabazio hori zerbitzari publikoetara bidaltzen da. + +Aukeratu non prozesatzen den ahotsa: +" +TITLE="Open Voice OS instalazioa - Ahotsa" +LOCAL_DESCRIPTION="Prozesatu ahotsa makina honetan" +PUBLIC_DESCRIPTION="Erabili Open Voice OS-en zerbitzari publikoak" + +export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/eu-es/summary.sh b/tui/locales/eu-es/summary.sh index 9e791544..76a7db19 100644 --- a/tui/locales/eu-es/summary.sh +++ b/tui/locales/eu-es/summary.sh @@ -9,6 +9,7 @@ Ia amaitu duzu, hona hemen Open Voice OS instalatzeko egin dituzun aukeren labur - Trebetasun gehigarriak: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-} - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-} - LLM: ${LLM_SUMMARY_STATE:-} + - Ahotsa: ${SPEECH_SUMMARY_STATE:-} - Afinazioa: ${TUNING_SUMMARY_STATE:-} Open Voice OS instalazio-prozesuan egindako aukerak arretaz aztertu dira gure sistema zure behar eta lehentasun berezietara egokitzeko. @@ -22,5 +23,10 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" +SUMMARY_SPEECH_LOCAL="makina honetan" +SUMMARY_SPEECH_PUBLIC="zerbitzari publikoak" +SUMMARY_SPEECH_PUBLIC_HARDWARE="zerbitzari publikoak (lokala: 8 GB-ko Pi 5 edo hobea)" +SUMMARY_SPEECH_PUBLIC_SETUP="zerbitzari publikoak (lokala: alpha eta virtualenv)" +SUMMARY_SPEECH_UNUSED="profil honek ez du erabiltzen" -export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION +export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/fr-fr/speech.sh b/tui/locales/fr-fr/speech.sh new file mode 100644 index 00000000..2cb27472 --- /dev/null +++ b/tui/locales/fr-fr/speech.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +CONTENT=" +Open Voice OS transforme ce que vous dites en texte (reconnaissance vocale) et lit ses réponses à voix haute (synthèse vocale). Cette machine est assez puissante pour faire les deux elle-même. + +- local : tout fonctionne sur cette machine. Votre voix reste ici, et rien ne dépend d'Internet ni de la charge des serveurs. L'installateur télécharge les modèles vocaux de votre langue, de quelques centaines de mégaoctets à quelques gigaoctets, et la reconnaissance occupe le processeur pendant que vous parlez. +- public : les serveurs publics d'Open Voice OS font le travail. Rien à télécharger, mais chaque requête passe par Internet vers des serveurs partagés par toute la communauté, ce qui ajoute du délai réseau et ralentit quand beaucoup de monde les utilise en même temps. Vos enregistrements et les réponses sont traités sur ces serveurs. + +Si la reconnaissance locale échoue ou n'entend rien, cet enregistrement-là est envoyé aux serveurs publics à la place. + +Veuillez sélectionner où la parole est traitée : +" +TITLE="Open Voice OS Installation - Traitement de la parole" +LOCAL_DESCRIPTION="Traiter la parole sur cette machine" +PUBLIC_DESCRIPTION="Utiliser les serveurs publics d'Open Voice OS" + +export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/fr-fr/summary.sh b/tui/locales/fr-fr/summary.sh index 04845d61..771ff81b 100644 --- a/tui/locales/fr-fr/summary.sh +++ b/tui/locales/fr-fr/summary.sh @@ -9,6 +9,7 @@ Vous y êtes presque ! Voici un résumé des choix que vous avez effectués pour - Compétences supplémentaires : ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-} - Home Assistant : ${HOMEASSISTANT_SUMMARY_STATE:-} - LLM : ${LLM_SUMMARY_STATE:-} + - Traitement de la parole : ${SPEECH_SUMMARY_STATE:-} - Réglages Raspberry Pi : ${TUNING_SUMMARY_STATE:-} Les choix effectués lors du processus d'installation d'Open Voice OS ont été soigneusement étudiés pour adapter notre système à vos besoins et préférences. @@ -22,5 +23,10 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" +SUMMARY_SPEECH_LOCAL="sur cette machine" +SUMMARY_SPEECH_PUBLIC="serveurs publics" +SUMMARY_SPEECH_PUBLIC_HARDWARE="serveurs publics (local : Pi 5 8 Go minimum)" +SUMMARY_SPEECH_PUBLIC_SETUP="serveurs publics (local : alpha et virtualenv)" +SUMMARY_SPEECH_UNUSED="non utilisé par ce profil" -export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION +export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/gl-es/speech.sh b/tui/locales/gl-es/speech.sh new file mode 100644 index 00000000..149bd72a --- /dev/null +++ b/tui/locales/gl-es/speech.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +CONTENT=" +Open Voice OS converte o que dis en texto (recoñecemento de voz) e le as súas respostas en voz alta (síntese de voz). Este equipo é abondo potente para facer as dúas cousas el mesmo. + + - local: todo se executa neste equipo. A túa voz queda aquí e nada depende de internet nin de canto de ocupados estean os servidores. O instalador descarga os modelos de voz do teu idioma, desde uns centos de megabytes ata uns poucos gigabytes, e o recoñecemento mantén ocupado o procesador mentres falas. + - public: os servidores públicos de Open Voice OS fan o traballo. Non hai nada que descargar, pero cada petición viaxa por internet ata servidores que comparte toda a comunidade, o que engade atraso de rede e vólvese máis lento cando moita xente os usa á vez. As túas gravacións e as respostas procésanse neses servidores. + +Se o recoñecemento local falla ou non oe nada, esa gravación envíase aos servidores públicos. + +Selecciona onde se procesa a voz: +" +TITLE="Instalación de Open Voice OS - Voz" +LOCAL_DESCRIPTION="Procesar a voz neste equipo" +PUBLIC_DESCRIPTION="Usar os servidores públicos de Open Voice OS" + +export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/gl-es/summary.sh b/tui/locales/gl-es/summary.sh index d742d6cc..e9540284 100644 --- a/tui/locales/gl-es/summary.sh +++ b/tui/locales/gl-es/summary.sh @@ -9,6 +9,7 @@ Estás a piques de rematar. Aquí tes un resumo das opcións que escolliches par - Habilidades adicionais: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-} - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-} - LLM: ${LLM_SUMMARY_STATE:-} + - Voz: ${SPEECH_SUMMARY_STATE:-} - Axustes: ${TUNING_SUMMARY_STATE:-} As decisións tomadas durante a instalación de Open Voice OS foron coidadosamente pensadas para adaptar o noso sistema ás túas necesidades e preferencias. @@ -22,5 +23,10 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" +SUMMARY_SPEECH_LOCAL="neste equipo" +SUMMARY_SPEECH_PUBLIC="servidores públicos" +SUMMARY_SPEECH_PUBLIC_HARDWARE="servidores públicos (local: Pi 5 de 8 GB ou mellor)" +SUMMARY_SPEECH_PUBLIC_SETUP="servidores públicos (local: alpha e virtualenv)" +SUMMARY_SPEECH_UNUSED="este perfil non a usa" -export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION +export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/hi-in/speech.sh b/tui/locales/hi-in/speech.sh new file mode 100644 index 00000000..89545ed1 --- /dev/null +++ b/tui/locales/hi-in/speech.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +CONTENT=" +Open Voice OS आपकी कही बात को टेक्स्ट में बदलता है (वाक् पहचान) और अपने जवाब बोलकर सुनाता है (वाक् संश्लेषण)। यह मशीन ये दोनों काम ख़ुद करने के लिए काफ़ी शक्तिशाली है। + + - local: सब कुछ इसी मशीन पर चलता है। आपकी आवाज़ यहीं रहती है, और कुछ भी इंटरनेट या सर्वरों की व्यस्तता पर निर्भर नहीं करता। इंस्टॉलर आपकी भाषा के वाक् मॉडल डाउनलोड करता है, जो कुछ सौ मेगाबाइट से लेकर कुछ गीगाबाइट तक के होते हैं, और आपके बोलते समय पहचान प्रोसेसर को व्यस्त रखती है। + - public: Open Voice OS के सार्वजनिक सर्वर यह काम करते हैं। कुछ डाउनलोड नहीं करना पड़ता, लेकिन हर अनुरोध इंटरनेट से होकर उन सर्वरों तक जाता है जिन्हें पूरा समुदाय साझा करता है। इससे नेटवर्क की देरी जुड़ती है, और जब बहुत से लोग एक साथ इनका उपयोग करते हैं तो जवाब धीमे हो जाते हैं। आपकी रिकॉर्डिंग और जवाब इन्हीं सर्वरों पर प्रोसेस होते हैं। + +अगर लोकल पहचान विफल हो जाए या कुछ न सुन पाए, तो वह रिकॉर्डिंग सार्वजनिक सर्वरों पर भेज दी जाती है। + +कृपया चुनें कि आवाज़ कहाँ प्रोसेस हो: +" +TITLE="Open Voice OS Installation - आवाज़" +LOCAL_DESCRIPTION="इसी मशीन पर आवाज़ प्रोसेस करें" +PUBLIC_DESCRIPTION="Open Voice OS के सार्वजनिक सर्वर इस्तेमाल करें" + +export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/hi-in/summary.sh b/tui/locales/hi-in/summary.sh index a9fbeadc..58685700 100644 --- a/tui/locales/hi-in/summary.sh +++ b/tui/locales/hi-in/summary.sh @@ -8,6 +8,7 @@ CONTENT=" - अतिरिक्त कौशल: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-} - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-} - LLM: ${LLM_SUMMARY_STATE:-} + - आवाज़: ${SPEECH_SUMMARY_STATE:-} - ट्यूनिंग: ${TUNING_SUMMARY_STATE:-} ओपन वॉयस ओएस (Open Voice OS) इंस्टॉलेशन प्रक्रिया के दौरान चुने गए विकल्पों पर हमारे सिस्टम (System) को आपकी विशिष्ट आवश्यकताओं और प्राथमिकताओं के अनुरूप बनाने के लिए सावधानीपूर्वक विचार किया गया है। @@ -20,5 +21,10 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" +SUMMARY_SPEECH_LOCAL="इसी मशीन पर" +SUMMARY_SPEECH_PUBLIC="सार्वजनिक सर्वर" +SUMMARY_SPEECH_PUBLIC_HARDWARE="सार्वजनिक सर्वर (लोकल: 8 GB वाला Pi 5 या बेहतर)" +SUMMARY_SPEECH_PUBLIC_SETUP="सार्वजनिक सर्वर (लोकल: alpha और virtualenv)" +SUMMARY_SPEECH_UNUSED="यह प्रोफ़ाइल इसका उपयोग नहीं करती" -export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION +export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/it-it/speech.sh b/tui/locales/it-it/speech.sh new file mode 100644 index 00000000..6965f9ec --- /dev/null +++ b/tui/locales/it-it/speech.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +CONTENT=" +Open Voice OS trasforma ciò che dici in testo (riconoscimento vocale) e legge le risposte ad alta voce (sintesi vocale). Questa macchina è abbastanza potente da fare entrambe le cose da sola. + + - local: tutto gira su questa macchina. La tua voce resta qui e nulla dipende da internet o da quanto sono occupati i server. L'installer scarica i modelli vocali per la tua lingua, da qualche centinaio di megabyte a qualche gigabyte, e il riconoscimento tiene occupato il processore mentre parli. + - public: il lavoro lo fanno i server pubblici di Open Voice OS. Niente da scaricare, ma ogni richiesta viaggia su internet verso server condivisi da tutta la comunità: questo aggiunge ritardo di rete e rallenta quando molte persone li usano contemporaneamente. Le tue registrazioni e le risposte vengono elaborate su quei server. + +Se il riconoscimento locale fallisce o non sente nulla, quella registrazione viene inviata ai server pubblici. + +Seleziona dove elaborare la voce: +" +TITLE="Installazione di Open Voice OS - Voce" +LOCAL_DESCRIPTION="Elaborare la voce su questa macchina" +PUBLIC_DESCRIPTION="Usare i server pubblici di Open Voice OS" + +export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/it-it/summary.sh b/tui/locales/it-it/summary.sh index 14cd999a..24fefa5a 100644 --- a/tui/locales/it-it/summary.sh +++ b/tui/locales/it-it/summary.sh @@ -9,6 +9,7 @@ Abbiamo quasi finito. Qui c'è un riassunto delle opzioni scelte per installare - Competenze extra: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-} - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-} - LLM: ${LLM_SUMMARY_STATE:-} + - Voce: ${SPEECH_SUMMARY_STATE:-} - Ottimizzazione: ${TUNING_SUMMARY_STATE:-} Le decisioni prese durante il processo di installazione di Open Voice OS sono state attentamente valutate per personalizzare il nostro sistema in base alle tue esigenze e preferenze individuali. @@ -22,5 +23,10 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" +SUMMARY_SPEECH_LOCAL="su questa macchina" +SUMMARY_SPEECH_PUBLIC="server pubblici" +SUMMARY_SPEECH_PUBLIC_HARDWARE="server pubblici (locale: Pi 5 con 8 GB o superiore)" +SUMMARY_SPEECH_PUBLIC_SETUP="server pubblici (locale: alpha e virtualenv)" +SUMMARY_SPEECH_UNUSED="non usata da questo profilo" -export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION +export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/kab-dz/speech.sh b/tui/locales/kab-dz/speech.sh new file mode 100644 index 00000000..9ae47111 --- /dev/null +++ b/tui/locales/kab-dz/speech.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +CONTENT=" +Open Voice OS yettbeddil ayen i d-tenniḍ ɣer uḍris (aɛqal n taɣect) yerna yeqqar-d tiririyin-is s taɣect (asuddes n taɣect). Tamacint-a tezmer ad txeddem snat-a s yiman-is. + + - local: kullec ad iteddu ɣef tmacint-a. Taɣect-ik ad teqqim da, ulac ayen icudden ɣer Internet neɣ ɣer yiqeddacen. Amesbeddi ad d-yessader timudmin n taɣect n tutlayt-ik, seg kra n yimiḍ n MB alamma d kra n GB, yerna aɛqal ad yesseqdec amesbadu mi ara tettmeslayeḍ. + - public: d iqeddacen izayazen n Open Voice OS ara ixedmen axeddim. Ulac ayen ara d-tessadreḍ, maca yal asuter ad yeddu s Internet ɣer yiqeddacen i tebḍa merra temɣiwant: aya yettarra-d aɛettel, yerna ad ẓẓayen mi ara ten-sqedcen aṭas n medden ɣef tikkelt. Iseklasen-ik d tririyin ad ttwasenfaren ɣef yiqeddacen-a. + +Ma yecceḍ uɛqal adigan neɣ ur yesla i kra, asekles-nni ad yettwazen ɣer yiqeddacen izayazen. + +Ttxil-k fren anida ara tettwasenfar taɣect: +" +TITLE="Asbeddi n Open Voice OS - Taɣect" +LOCAL_DESCRIPTION="Taɣect ɣef tmacint-a" +PUBLIC_DESCRIPTION="Iqeddacen izayazen n Open Voice OS" + +export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/kab-dz/summary.sh b/tui/locales/kab-dz/summary.sh index 61cc493f..a7a08b64 100644 --- a/tui/locales/kab-dz/summary.sh +++ b/tui/locales/kab-dz/summary.sh @@ -9,6 +9,7 @@ Qrib ad tekfuḍ, ha-t-an ugzul n textiṛiyin i tgiḍ akken ad tesbeddeḍ ana - Tussniwin timernanin: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-} - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-} - LLM: ${LLM_SUMMARY_STATE:-} + - Taɣect: ${SPEECH_SUMMARY_STATE:-} - Tamellit: ${TUNING_SUMMARY_STATE:-} Ifranen i d-yettwaxedmen deg ukala n usbeddi n Open Voice OS ttwafernen s ttawil akked ufeṣṣel i wakken ad yeddu unagraw-nneɣ akken i teḥwaǧeḍ. @@ -22,5 +23,10 @@ SUMMARY_STATE_DISABLED="d arurmid" SUMMARY_STATE_UNSUPPORTED_PROFILE="yettawfran (ur yettwasefrek ara deg umaɣnu-a)" SUMMARY_STATE_MISSING_URL="yettwafran (txuṣṣ URL; as yettwazgal)" SUMMARY_STATE_MISSING_CONFIGURATION="yettwafran (txuṣṣ tawila; as yettwazgal)" +SUMMARY_SPEECH_LOCAL="ɣef tmacint-a" +SUMMARY_SPEECH_PUBLIC="iqeddacen izayazen" +SUMMARY_SPEECH_PUBLIC_HARDWARE="iqeddacen izayazen (adigan: Pi 5 s 8 GB neɣ ugar)" +SUMMARY_SPEECH_PUBLIC_SETUP="iqeddacen izayazen (adigan: alpha d virtualenv)" +SUMMARY_SPEECH_UNUSED="ur yettwaseqdac ara deg umaɣnu-a" -export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION +export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/nl-nl/speech.sh b/tui/locales/nl-nl/speech.sh new file mode 100644 index 00000000..d21fe7c2 --- /dev/null +++ b/tui/locales/nl-nl/speech.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +CONTENT=" +Open Voice OS zet wat je zegt om in tekst (spraakherkenning) en leest zijn antwoorden voor (spraaksynthese). Deze machine is krachtig genoeg om beide zelf te doen. + +- local: alles draait op deze machine. Je stem blijft hier, en niets hangt af van internet of van hoe druk de servers zijn. Het installatieprogramma downloadt de spraakmodellen voor je taal, van een paar honderd megabyte tot een paar gigabyte, en de herkenning houdt de processor bezig terwijl je praat. +- public: de openbare servers van Open Voice OS doen het werk. Je hoeft niets te downloaden, maar elk verzoek gaat via internet naar servers die de hele community deelt. Dat voegt netwerkvertraging toe en wordt trager wanneer veel mensen ze tegelijk gebruiken. Je opnames en de antwoorden worden op die servers verwerkt. + +Als de lokale herkenning mislukt of niets hoort, wordt die ene opname naar de openbare servers gestuurd. + +Selecteer waar spraak wordt verwerkt: +" +TITLE="Open Voice OS Installatie - Spraak" +LOCAL_DESCRIPTION="Spraak op deze machine verwerken" +PUBLIC_DESCRIPTION="De openbare Open Voice OS-servers gebruiken" + +export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/nl-nl/summary.sh b/tui/locales/nl-nl/summary.sh index fe962e12..0b436bf5 100644 --- a/tui/locales/nl-nl/summary.sh +++ b/tui/locales/nl-nl/summary.sh @@ -9,6 +9,7 @@ Bijna klaar. Een korte samenvatting: - Extra skills: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-} - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-} - LLM: ${LLM_SUMMARY_STATE:-} + - Spraak: ${SPEECH_SUMMARY_STATE:-} - Tuning: ${TUNING_SUMMARY_STATE:-} De beslissingen die zijn genomen tijdens het installatieproces van OpenVoice OS zijn zorgvuldig gemaakt om ons systeem aan te passen aan je individuele behoeften en voorkeuren. @@ -22,5 +23,10 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" +SUMMARY_SPEECH_LOCAL="op deze machine" +SUMMARY_SPEECH_PUBLIC="openbare servers" +SUMMARY_SPEECH_PUBLIC_HARDWARE="openbare servers (lokaal: Pi 5 met 8 GB of beter)" +SUMMARY_SPEECH_PUBLIC_SETUP="openbare servers (lokaal: alpha en virtualenv)" +SUMMARY_SPEECH_UNUSED="niet gebruikt door dit profiel" -export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION +export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/pl-pl/speech.sh b/tui/locales/pl-pl/speech.sh new file mode 100644 index 00000000..6761afd0 --- /dev/null +++ b/tui/locales/pl-pl/speech.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +CONTENT=" +Open Voice OS zamienia to, co mówisz, na tekst (rozpoznawanie mowy) i czyta swoje odpowiedzi na głos (synteza mowy). Ten komputer jest na tyle wydajny, żeby robić jedno i drugie samodzielnie. + +- local: wszystko działa na tym komputerze. Twój głos zostaje tutaj i nic nie zależy od internetu ani od obciążenia serwerów. Instalator pobiera modele mowy dla Twojego języka, od kilkuset megabajtów do kilku gigabajtów, a rozpoznawanie obciąża procesor, gdy mówisz. +- public: pracę wykonują publiczne serwery Open Voice OS. Nic nie trzeba pobierać, ale każde żądanie wędruje przez internet do serwerów współdzielonych przez całą społeczność, co dodaje opóźnienie sieciowe i spowalnia działanie, gdy korzysta z nich wiele osób naraz. Twoje nagrania i odpowiedzi są przetwarzane na tych serwerach. + +Jeśli lokalne rozpoznawanie zawiedzie lub nic nie usłyszy, to nagranie zostanie wysłane do serwerów publicznych. + +Wybierz, gdzie ma być przetwarzana mowa: +" +TITLE="Instalacja Open Voice OS - Mowa" +LOCAL_DESCRIPTION="Przetwarzaj mowę na tym komputerze" +PUBLIC_DESCRIPTION="Używaj publicznych serwerów Open Voice OS" + +export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/pl-pl/summary.sh b/tui/locales/pl-pl/summary.sh index 2a0987f8..724bb275 100644 --- a/tui/locales/pl-pl/summary.sh +++ b/tui/locales/pl-pl/summary.sh @@ -9,6 +9,7 @@ Już prawie skończyłeś, oto podsumowanie wyborów dokonanych podczas instalac - Dodatkowe umiejętności: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-} - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-} - LLM: ${LLM_SUMMARY_STATE:-} +- Mowa: ${SPEECH_SUMMARY_STATE:-} - Strojenie: ${TUNING_SUMMARY_STATE:-} Wybory dokonane podczas instalacji Open Voice OS zostały starannie rozważone, aby dostosować nasz system do Twoich unikalnych potrzeb i preferencji. @@ -22,5 +23,10 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" +SUMMARY_SPEECH_LOCAL="na tym komputerze" +SUMMARY_SPEECH_PUBLIC="serwery publiczne" +SUMMARY_SPEECH_PUBLIC_HARDWARE="serwery publiczne (lokalnie: Pi 5 z 8 GB lub lepszy)" +SUMMARY_SPEECH_PUBLIC_SETUP="serwery publiczne (lokalnie: alpha i virtualenv)" +SUMMARY_SPEECH_UNUSED="nieużywana w tym profilu" -export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION +export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/pt-pt/speech.sh b/tui/locales/pt-pt/speech.sh new file mode 100644 index 00000000..b5cf7fd6 --- /dev/null +++ b/tui/locales/pt-pt/speech.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +CONTENT=" +O Open Voice OS converte o que diz em texto (reconhecimento de voz) e lê as respostas em voz alta (síntese de voz). Esta máquina é suficientemente potente para fazer as duas coisas sozinha. + + - local: tudo corre nesta máquina. A sua voz fica aqui e nada depende da internet nem da ocupação dos servidores. O instalador descarrega os modelos de voz do seu idioma, de algumas centenas de megabytes a alguns gigabytes, e o reconhecimento mantém o processador ocupado enquanto fala. + - public: os servidores públicos do Open Voice OS fazem o trabalho. Não há nada para descarregar, mas cada pedido viaja pela internet até servidores partilhados por toda a comunidade, o que acrescenta atraso de rede e fica mais lento quando muitas pessoas os usam ao mesmo tempo. As suas gravações e as respostas são processadas nesses servidores. + +Se o reconhecimento local falhar ou não ouvir nada, essa gravação é enviada para os servidores públicos. + +Seleccione onde a voz é processada: +" +TITLE="Open Voice OS Instalação - Voz" +LOCAL_DESCRIPTION="Processar a voz nesta máquina" +PUBLIC_DESCRIPTION="Usar os servidores públicos do Open Voice OS" + +export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/pt-pt/summary.sh b/tui/locales/pt-pt/summary.sh index dc5bc9f7..ef9b154f 100644 --- a/tui/locales/pt-pt/summary.sh +++ b/tui/locales/pt-pt/summary.sh @@ -9,6 +9,7 @@ Quase a terminar. Um breve resumo: - Skills extra: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-} - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-} - LLM: ${LLM_SUMMARY_STATE:-} + - Voz: ${SPEECH_SUMMARY_STATE:-} - Otimização: ${TUNING_SUMMARY_STATE:-} As decisões tomadas durante o processo de instalação do Open Voice OS foram cuidadosamente consideradas para personalizar o nosso sistema de acordo com as suas necessidades e preferências individuais. @@ -22,5 +23,10 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" +SUMMARY_SPEECH_LOCAL="nesta máquina" +SUMMARY_SPEECH_PUBLIC="servidores públicos" +SUMMARY_SPEECH_PUBLIC_HARDWARE="servidores públicos (local: Pi 5 com 8 GB ou melhor)" +SUMMARY_SPEECH_PUBLIC_SETUP="servidores públicos (local: alpha e virtualenv)" +SUMMARY_SPEECH_UNUSED="não utilizado por este perfil" -export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION +export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/main.sh b/tui/main.sh index cde9abeb..b93e830f 100644 --- a/tui/main.sh +++ b/tui/main.sh @@ -81,6 +81,10 @@ while [ "$tui_step_index" -lt "${#TUI_FLOW[@]}" ]; do # shellcheck source=tui/satellite/main.sh source tui/satellite/main.sh ;; + speech) + # shellcheck source=tui/speech.sh + source tui/speech.sh + ;; tuning) # shellcheck source=tui/tuning.sh source tui/tuning.sh diff --git a/tui/navigation.sh b/tui/navigation.sh index 778ddac3..b86e5b77 100644 --- a/tui/navigation.sh +++ b/tui/navigation.sh @@ -16,6 +16,8 @@ # shellcheck source=tui/dialogs.sh source tui/dialogs.sh +# shellcheck source=utils/speech.sh +source utils/speech.sh TUI_NAV="${TUI_NAV:-next}" export TUI_NAV @@ -34,6 +36,7 @@ declare -a TUI_FLOW=( profiles features satellite + speech tuning summary telemetry @@ -153,6 +156,9 @@ function tui_flow_step_enabled() { satellite) [[ "${PROFILE:-}" == "satellite" ]] ;; + speech) + local_speech_available + ;; tuning) [[ "${RASPBERRYPI_MODEL:-N/A}" != "N/A" ]] ;; diff --git a/tui/speech.sh b/tui/speech.sh new file mode 100644 index 00000000..3a4cad7a --- /dev/null +++ b/tui/speech.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# shellcheck source=tui/navigation.sh +source tui/navigation.sh +# shellcheck source=tui/locales/en-us/speech.sh +source "tui/locales/$LOCALE/speech.sh" + +# The flow only shows this screen when local_speech_available, so both choices +# can actually be installed. Local is what the hardware was checked for. +active_engine="local" +if [ -f "$INSTALLER_STATE_FILE" ]; then + saved_engine="$(jq -r '.speech_engine // ""' "$INSTALLER_STATE_FILE" 2>>"$LOG_FILE")" + case "$saved_engine" in + local|public) + active_engine="$saved_engine" + ;; + esac +fi + +whiptail_args=( + --title "$TITLE" + --radiolist "$CONTENT" + --cancel-button "$BACK_BUTTON" + --ok-button "$OK_BUTTON" + "$TUI_WINDOW_HEIGHT" "$TUI_WINDOW_WIDTH" 4 +) + +for engine in local public; do + if [ "$engine" == "local" ]; then + whiptail_args+=("$engine" "$LOCAL_DESCRIPTION") + else + whiptail_args+=("$engine" "$PUBLIC_DESCRIPTION") + fi + if [ "$engine" == "$active_engine" ]; then + whiptail_args+=("ON") + else + whiptail_args+=("OFF") + fi +done + +if ! tui_nav_capture SPEECH_ENGINE "${whiptail_args[@]}"; then + SPEECH_ENGINE="$active_engine" + export SPEECH_ENGINE + return 0 +fi +export SPEECH_ENGINE + +# Persist selection (used for defaults when navigating back or re-running). +state_tmp="$(mktemp)" +if [ -f "$INSTALLER_STATE_FILE" ] && \ + jq --arg speech_engine "$SPEECH_ENGINE" \ + 'if type=="object" then . else {} end | .speech_engine = $speech_engine' \ + "$INSTALLER_STATE_FILE" >"$state_tmp" 2>>"$LOG_FILE"; then + mv -f "$state_tmp" "$INSTALLER_STATE_FILE" +else + jq -n --arg speech_engine "$SPEECH_ENGINE" '{speech_engine: $speech_engine}' >"$state_tmp" 2>>"$LOG_FILE" && \ + mv -f "$state_tmp" "$INSTALLER_STATE_FILE" +fi + +# Keep state writable by the target user when running under sudo/root. +if [ -n "${RUN_AS:-}" ] && [ -f "$INSTALLER_STATE_FILE" ]; then + chown "$RUN_AS":"$(id -ng "$RUN_AS" 2>>"$LOG_FILE")" "$INSTALLER_STATE_FILE" &>>"$LOG_FILE" || true +fi diff --git a/tui/summary.sh b/tui/summary.sh index 5489f9bd..c145f960 100644 --- a/tui/summary.sh +++ b/tui/summary.sh @@ -43,12 +43,31 @@ function summary_feature_state() { printf '%s\n' "$incomplete_state" } +# What the playbook will do with speech, which is not always what was picked: +# normalize_speech_engine keeps a run that cannot use local speech on public. +function summary_speech_state() { + if [ "${PROFILE:-}" == "server" ]; then + printf '%s\n' "${SUMMARY_SPEECH_UNUSED:-not used by this profile}" + elif local_speech_available; then + if [ "${SPEECH_ENGINE:-}" == "local" ]; then + printf '%s\n' "${SUMMARY_SPEECH_LOCAL:-on this machine}" + else + printf '%s\n' "${SUMMARY_SPEECH_PUBLIC:-public servers}" + fi + elif [ "${LOCAL_SPEECH_CAPABLE:-false}" != "true" ]; then + printf '%s\n' "${SUMMARY_SPEECH_PUBLIC_HARDWARE:-public servers}" + else + printf '%s\n' "${SUMMARY_SPEECH_PUBLIC_SETUP:-public servers}" + fi +} + # Recomputed on every pass: the user can go back, change a choice, and come # straight back to this screen. function summary_refresh_states() { FEATURE_SKILLS_SUMMARY_STATE="$(summary_toggle_state "${FEATURE_SKILLS:-false}")" FEATURE_EXTRA_SKILLS_SUMMARY_STATE="$(summary_toggle_state "${FEATURE_EXTRA_SKILLS:-false}")" TUNING_SUMMARY_STATE="$(summary_toggle_state "${TUNING:-no}")" + SPEECH_SUMMARY_STATE="$(summary_speech_state)" local homeassistant_configured="false" if [ -n "${HOMEASSISTANT_URL:-}" ]; then @@ -78,6 +97,7 @@ function summary_refresh_states() { export FEATURE_SKILLS_SUMMARY_STATE="" export FEATURE_EXTRA_SKILLS_SUMMARY_STATE="" export TUNING_SUMMARY_STATE="" +export SPEECH_SUMMARY_STATE="" export HOMEASSISTANT_SUMMARY_STATE="" export LLM_SUMMARY_STATE="" diff --git a/utils/constants.sh b/utils/constants.sh index 4de3b592..6c79213d 100644 --- a/utils/constants.sh +++ b/utils/constants.sh @@ -20,6 +20,7 @@ export EXIT_PERMISSION_DENIED=2 export EXIT_SUCCESS=0 export I2C_BUS="1" export INSTALLER_VENV_NAME="ovos-installer" +export LOCAL_SPEECH_MIN_MEMORY_MB=7680 export LOG_FILE=/var/log/ovos-installer.log export NEWT_COLORS=" root=white,black @@ -54,6 +55,8 @@ else fi export PASTE_URL="https://paste.uoi.io" export PULSE_SOCKET_WSL2=/mnt/wslg/PulseServer +# Raspberry Pi 5, Pi 500 and Compute Module 5, as the device tree names them. +export RASPBERRY_PI_5_FAMILY_REGEX='(^|[[:space:]])Raspberry[[:space:]]Pi[[:space:]](5|500|Compute[[:space:]]Module[[:space:]]5)([^0-9]|$)' export REBOOT_FILE_PATH=/tmp/ovos.reboot declare -ra SCENARIO_ALLOWED_FEATURES=(skills extra_skills gui homeassistant llm) export SCENARIO_ALLOWED_FEATURES @@ -61,7 +64,7 @@ declare -ra SCENARIO_ALLOWED_HIVEMIND_OPTIONS=(host port key password) export SCENARIO_ALLOWED_HIVEMIND_OPTIONS declare -ra SCENARIO_ALLOWED_LLM_OPTIONS=(api_url key model persona max_tokens temperature top_p) export SCENARIO_ALLOWED_LLM_OPTIONS -declare -ra SCENARIO_ALLOWED_OPTIONS=(features channel hardware share_telemetry share_usage_telemetry profile method uninstall raspberry_pi_tuning hivemind llm) +declare -ra SCENARIO_ALLOWED_OPTIONS=(features channel hardware share_telemetry share_usage_telemetry profile method uninstall raspberry_pi_tuning hivemind llm speech_engine) export SCENARIO_ALLOWED_OPTIONS export SCENARIO_NAME="scenario.yaml" export SCENARIO_PATH="" diff --git a/utils/scenario.sh b/utils/scenario.sh index e301189d..a7d53a7d 100644 --- a/utils/scenario.sh +++ b/utils/scenario.sh @@ -160,6 +160,17 @@ if [ -f "$SCENARIO_PATH" ]; then fi export TUNING ;; + speech_engine) + case "${options[$option]}" in + local | public) + export SPEECH_ENGINE="${options[$option]}" + ;; + *) + scenario_reject "speech_engine" "${options[$option]}" "local, public" + break + ;; + esac + ;; features) for feature in "${!features[@]}"; do # Ensure the feature is supported by the installer diff --git a/utils/speech.sh b/utils/speech.sh new file mode 100644 index 00000000..503933ea --- /dev/null +++ b/utils/speech.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# Where speech recognition (STT) and speech synthesis (TTS) run. +# +# public the Open Voice OS defaults: both go to the community servers. +# local the onnx-asr and phoonnx plugins ovos-config recommends for the +# locale run on this host, with the public STT server as fallback. +# +# Local speech only gives a usable assistant on a Raspberry Pi 5 with 8 GB or a +# machine at least as capable, so everything below that is kept on public. + +# Total memory in MiB, or 0 when it cannot be read. +function total_memory_mb() { + local memory_bytes="" + + case "$(uname -s 2>/dev/null || true)" in + Darwin) + memory_bytes="$(sysctl -n hw.memsize 2>/dev/null || true)" + if [[ "$memory_bytes" =~ ^[0-9]+$ ]]; then + printf '%s\n' "$((memory_bytes / 1048576))" + return 0 + fi + ;; + *) + if [ -r /proc/meminfo ]; then + awk '/^MemTotal:/ { printf "%d\n", $2 / 1024; found = 1 } END { if (!found) print 0 }' /proc/meminfo + return 0 + fi + ;; + esac + + printf '%s\n' "0" +} + +# Whether this host can run local speech at all, whatever the user picks later. +# +# No Raspberry Pi other than the Pi 5 family qualifies, whatever its memory: a +# Pi 4 with 8 GB passes the memory test and still cannot keep up. Any other +# machine needs the SIMD instructions onnxruntime is built for and a 64-bit +# userland, since onnxruntime publishes no 32-bit wheels and none for Intel +# Macs. An 8 GB board reports less than 8192 MiB once firmware and kernel have +# taken their share, which is why the floor sits at 7.5 GiB. +function local_speech_hardware_supported() { + [ "${CPU_IS_CAPABLE:-false}" == "true" ] || return 1 + [ "$(getconf LONG_BIT 2>/dev/null || echo 0)" == "64" ] || return 1 + + case "$(uname -s 2>/dev/null || true)-$(uname -m 2>/dev/null || true)" in + Linux-x86_64 | Linux-aarch64 | Darwin-arm64) ;; + *) return 1 ;; + esac + + if [ "${RASPBERRYPI_MODEL:-N/A}" != "N/A" ] && + ! [[ "$RASPBERRYPI_MODEL" =~ $RASPBERRY_PI_5_FAMILY_REGEX ]]; then + return 1 + fi + + [ "${TOTAL_MEMORY_MB:-0}" -ge "$LOCAL_SPEECH_MIN_MEMORY_MB" ] +} + +# Requires detect_cpu_instructions and is_raspberrypi_soc to have run. +function detect_local_speech_support() { + printf '%s' "➤ Checking local speech support... " + TOTAL_MEMORY_MB="$(total_memory_mb)" + export TOTAL_MEMORY_MB + + if local_speech_hardware_supported; then + export LOCAL_SPEECH_CAPABLE="true" + else + export LOCAL_SPEECH_CAPABLE="false" + fi + echo -e "[${done_format:-done}]" +} + +# Capable hardware is not enough. Local speech is only offered on the alpha +# channel, the container images do not carry its plugins, and a server has no +# audio to process. +function local_speech_available() { + [ "${LOCAL_SPEECH_CAPABLE:-false}" == "true" ] && + [ "${CHANNEL:-}" == "alpha" ] && + [ "${METHOD:-}" == "virtualenv" ] && + [ "${PROFILE:-}" != "server" ] +} + +# Settle SPEECH_ENGINE before the playbook runs. A scenario file, an earlier +# run's state or a choice made before going back and changing the channel can +# all ask for local where it is not available; those runs get public. +function normalize_speech_engine() { + if [ "${SPEECH_ENGINE:-}" == "local" ] && ! local_speech_available; then + echo "Local speech needs the alpha channel, a virtualenv install, a profile with audio and a Raspberry Pi 5 with 8 GB or an equivalent machine. Using the public servers instead." | tee -a "$LOG_FILE" + fi + + if [ "${SPEECH_ENGINE:-}" == "local" ] && local_speech_available; then + export SPEECH_ENGINE="local" + else + export SPEECH_ENGINE="public" + fi +} From 3446fd6f6d5d2d4fd8fc4b3668367947d005684f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Tue, 6 Oct 2026 16:54:27 -0400 Subject: [PATCH 02/14] Exercise local speech through scenario files Ship an example scenario that asks for local speech, and give the nightly Linux matrix two alpha virtualenv runs that install it from a scenario file on the x86-64 and arm runners. They check that mycroft.conf names onnx-asr and phoonnx with the public server only as the STT fallback, that the model was fetched during the install, that the configured voice says "what time is it" and the configured recognizer hears it, and that the uninstall takes the models away. Every other cell pins speech_engine to public. Co-Authored-By: Claude Opus 5.5 --- .github/scripts/assert_local_speech.sh | 57 ++++++++++++++++++++++ .github/workflows/scenarios-ubuntu2404.yml | 31 ++++++++++++ docs/automation.md | 4 +- scenarios/scenario-local-speech.yml | 12 +++++ tests/bats/code_quality.bats | 23 +++++++++ 5 files changed, 126 insertions(+), 1 deletion(-) create mode 100755 .github/scripts/assert_local_speech.sh create mode 100644 scenarios/scenario-local-speech.yml diff --git a/.github/scripts/assert_local_speech.sh b/.github/scripts/assert_local_speech.sh new file mode 100755 index 00000000..a31d177c --- /dev/null +++ b/.github/scripts/assert_local_speech.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# Assert that a local speech install really runs speech on the device. +# +# The configuration has to name the onnx-asr and phoonnx plugins, with the public +# server only as the STT fallback; the STT model has to be on disk already, because +# the installer fetches it rather than leaving it to the first utterance; and the +# pair has to work: the configured voice says a sentence and the configured +# recognizer hears it back. Nothing goes to the public servers on the way. +# +# Never run this under sudo. HOME decides which mycroft.conf is read and where the +# models were downloaded, and both belong to the install user. +set -euo pipefail + +python_bin="${1:-${HOME}/.venvs/ovos/bin/python}" + +if [ ! -x "${python_bin}" ]; then + echo "no virtualenv python at ${python_bin}" >&2 + exit 1 +fi + +"${python_bin}" - <<'PY' +import os +import tempfile + +from ovos_config import Configuration +from ovos_plugin_manager.stt import load_stt_plugin +from ovos_plugin_manager.tts import load_tts_plugin +from ovos_plugin_manager.utils.audio import AudioData + +config = Configuration() +stt, tts, lang = config["stt"], config["tts"], config["lang"] + +if stt.get("module") != "ovos-stt-plugin-onnx-asr": + raise SystemExit(f"STT is {stt.get('module')!r}, not the local onnx-asr plugin") +if stt.get("fallback_module") != "ovos-stt-plugin-server": + raise SystemExit(f"STT fallback is {stt.get('fallback_module')!r}, not the public server") +if tts.get("module") != "ovos-tts-plugin-phoonnx": + raise SystemExit(f"TTS is {tts.get('module')!r}, not the local phoonnx plugin") + +models = os.path.expanduser("~/.local/share/ovos_stt_plugin_onnxasr") +if not os.path.isdir(models) or not os.listdir(models): + raise SystemExit(f"no STT model in {models}: the install did not fetch it") + +stt_config = dict(stt.get(stt["module"]) or {}, lang=lang) +tts_config = dict(tts.get(tts["module"]) or {}, lang=lang) + +wav = os.path.join(tempfile.mkdtemp(), "local-speech.wav") +load_tts_plugin(tts["module"])(config=tts_config).get_tts("what time is it", wav) +heard = load_stt_plugin(stt["module"])(config=stt_config).execute( + AudioData.from_file(wav), language=lang) or "" + +if "time" not in heard.lower(): + raise SystemExit(f"the voice said 'what time is it' and the recognizer heard {heard!r}") + +print(f"local speech ok: {stt_config.get('model')} heard {heard!r} " + f"from {tts_config.get('voice')}") +PY diff --git a/.github/workflows/scenarios-ubuntu2404.yml b/.github/workflows/scenarios-ubuntu2404.yml index f2ad46ec..75aad84a 100644 --- a/.github/workflows/scenarios-ubuntu2404.yml +++ b/.github/workflows/scenarios-ubuntu2404.yml @@ -226,6 +226,22 @@ jobs: feature_set: - minimal - all + speech_engine: + - public + # Local speech is alpha and virtualenv only. Both runners have the 16 GB and + # the AVX2/NEON the installer's hardware check asks for, so a run that falls + # back to public here is a regression, not a runner limit. + include: + - runner: ubuntu-24.04 + method: virtualenv + channel: alpha + feature_set: all + speech_engine: local + - runner: ubuntu-24.04-arm + method: virtualenv + channel: alpha + feature_set: all + speech_engine: local runs-on: ${{ matrix.runner }} timeout-minutes: 120 @@ -317,6 +333,7 @@ jobs: raspberry_pi_tuning: false share_telemetry: false share_usage_telemetry: false + speech_engine: ${{ matrix.speech_engine }} EOF - name: Run installer scenario exhaustive @@ -382,6 +399,10 @@ jobs: --utterance "what time is it" --expect-speak \ --ready-timeout 420 --reply-timeout 120 --speak-timeout 120 + - name: Validate speech runs on the device + if: matrix.speech_engine == 'local' + run: .github/scripts/assert_local_speech.sh "$HOME/.venvs/ovos/bin/python" + - name: Create uninstall scenario file shell: bash run: | @@ -403,6 +424,16 @@ jobs: - name: Run uninstall scenario exhaustive run: sudo -E env REUSE_CACHED_ARTIFACTS=true bash setup.sh + - name: Validate the uninstall removed the speech models + if: matrix.speech_engine == 'local' + run: | + for path in "$HOME/.local/share/ovos_stt_plugin_onnxasr" "$HOME/.cache/phoonnx"; do + if [ -e "$path" ]; then + echo "the uninstall left ${path} behind" >&2 + exit 1 + fi + done + - name: Dump diagnostics on failure if: failure() run: | diff --git a/docs/automation.md b/docs/automation.md index 7ea325e2..b8289ecc 100644 --- a/docs/automation.md +++ b/docs/automation.md @@ -27,7 +27,9 @@ EOF ``` Then run the installer as usual. More examples live in -[scenarios/](https://github.com/OpenVoiceOS/ovos-installer/tree/main/scenarios). +[scenarios/](https://github.com/OpenVoiceOS/ovos-installer/tree/main/scenarios), +including one that runs speech recognition and the voice on a Raspberry Pi 5 +([scenario-local-speech.yml](https://github.com/OpenVoiceOS/ovos-installer/blob/main/scenarios/scenario-local-speech.yml)). ## Scenario settings diff --git a/scenarios/scenario-local-speech.yml b/scenarios/scenario-local-speech.yml new file mode 100644 index 00000000..4ee93817 --- /dev/null +++ b/scenarios/scenario-local-speech.yml @@ -0,0 +1,12 @@ +--- +uninstall: false +method: virtualenv +channel: alpha +profile: ovos +features: + skills: true + extra_skills: false +raspberry_pi_tuning: true +share_telemetry: true +share_usage_telemetry: true +speech_engine: local diff --git a/tests/bats/code_quality.bats b/tests/bats/code_quality.bats index 9463dba8..1372b9fb 100644 --- a/tests/bats/code_quality.bats +++ b/tests/bats/code_quality.bats @@ -5163,3 +5163,26 @@ for template in ('core-requirements.txt.j2', 'satellite-requirements.txt.j2'): run grep -q "/.local/share/ovos_stt_plugin_onnxasr" ansible/roles/ovos_services/defaults/main.yml assert_success } + +@test "local_speech_is_exercised_by_scenarios" { + # The example scenario asks for local speech where it is available. + local scenario="scenarios/scenario-local-speech.yml" + run grep -qE '^speech_engine: local$' "$scenario" + assert_success + run grep -qE '^channel: alpha$' "$scenario" + assert_success + run grep -qE '^method: virtualenv$' "$scenario" + assert_success + + # The nightly matrix installs it through a scenario file, then checks that the + # device speaks and hears without the public servers and that the uninstall + # takes the models away. + local workflow=".github/workflows/scenarios-ubuntu2404.yml" + run grep -qF 'speech_engine: ${{ matrix.speech_engine }}' "$workflow" + assert_success + run grep -qF '.github/scripts/assert_local_speech.sh' "$workflow" + assert_success + run grep -qF 'the uninstall left ${path} behind' "$workflow" + assert_success + [ -x .github/scripts/assert_local_speech.sh ] +} From 6ffd465028bac299e5235d619819bfdc734af073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Tue, 6 Oct 2026 17:25:12 -0400 Subject: [PATCH 03/14] Say the public speech servers are goodwill, not a service They are run by the community as a backup and an example of self-hosted speech, and can go offline at any time. The speech screen now says so, and the public option reads as an acknowledgement of it rather than a feature. Co-Authored-By: Claude Opus 5.5 --- docs/automation.md | 2 +- translations/ca-es/strings.json | 4 ++-- translations/da/strings.json | 4 ++-- translations/de-de/strings.json | 4 ++-- translations/en-us/strings.json | 4 ++-- translations/es-es/strings.json | 4 ++-- translations/eu-es/strings.json | 4 ++-- translations/fr-fr/strings.json | 4 ++-- translations/gl-es/strings.json | 4 ++-- translations/hi-in/strings.json | 4 ++-- translations/it-it/strings.json | 4 ++-- translations/kab-dz/strings.json | 4 ++-- translations/nl-nl/strings.json | 4 ++-- translations/pl-pl/strings.json | 4 ++-- translations/pt-pt/strings.json | 4 ++-- tui/locales/ca-es/speech.sh | 4 ++-- tui/locales/da/speech.sh | 4 ++-- tui/locales/de-de/speech.sh | 4 ++-- tui/locales/en-us/speech.sh | 4 ++-- tui/locales/es-es/speech.sh | 4 ++-- tui/locales/eu-es/speech.sh | 4 ++-- tui/locales/fr-fr/speech.sh | 4 ++-- tui/locales/gl-es/speech.sh | 4 ++-- tui/locales/hi-in/speech.sh | 4 ++-- tui/locales/it-it/speech.sh | 4 ++-- tui/locales/kab-dz/speech.sh | 4 ++-- tui/locales/nl-nl/speech.sh | 4 ++-- tui/locales/pl-pl/speech.sh | 4 ++-- tui/locales/pt-pt/speech.sh | 4 ++-- 29 files changed, 57 insertions(+), 57 deletions(-) diff --git a/docs/automation.md b/docs/automation.md index b8289ecc..16d50a40 100644 --- a/docs/automation.md +++ b/docs/automation.md @@ -46,7 +46,7 @@ including one that runs speech recognition and the voice on a Raspberry Pi 5 | `llm.key` | API key for that endpoint, required with `features.llm` | | `llm.model` | Model name to use, required with `features.llm` | | `llm.persona` | System prompt for `ovos-persona`, required with `features.llm` | -| `speech_engine` | Where speech recognition and the voice run: `public` (the default) uses the Open Voice OS public servers, `local` runs them on the device. Local needs the `alpha` channel, the `virtualenv` method, a profile with audio and a Raspberry Pi 5 with 8 GB or an equivalent machine; anywhere else the installer says so and uses `public` | +| `speech_engine` | Where speech recognition and the voice run: `public` (the default) uses the community's public servers, which run on goodwill rather than as a production service and can go offline at any time; `local` runs them on the device. Local needs the `alpha` channel, the `virtualenv` method, a profile with audio and a Raspberry Pi 5 with 8 GB or an equivalent machine; anywhere else the installer says so and uses `public` | | `raspberry_pi_tuning` | Maximum-performance tuning for a Pi, including an overclocking prompt | | `share_telemetry` | Share anonymous usage statistics — see [Telemetry](telemetry.md) | | `share_usage_telemetry` | Share detailed usage data — see [Telemetry](telemetry.md) | diff --git a/translations/ca-es/strings.json b/translations/ca-es/strings.json index 115fc6b9..c9fe0e9c 100644 --- a/translations/ca-es/strings.json +++ b/translations/ca-es/strings.json @@ -58,9 +58,9 @@ }, "speech.sh": { "title": "Instal·lació de l'Open Voice OS - Veu", - "content": "L'Open Voice OS converteix el que dius en text (reconeixement de veu) i llegeix les respostes en veu alta (síntesi de veu). Aquest equip és prou potent per fer totes dues coses ell mateix.\n\n- local: tot s'executa en aquest equip. La teva veu es queda aquí i res no depèn d'internet ni de com d'ocupats estiguin els servidors. L'instal·lador baixa els models de veu del teu idioma, des d'uns quants centenars de megabytes fins a uns quants gigabytes, i el reconeixement manté ocupat el processador mentre parles.\n- public: els servidors públics de l'Open Voice OS fan la feina. No cal baixar res, però cada petició viatja per internet fins a servidors que comparteix tota la comunitat, cosa que hi afegeix retard de xarxa i que s'alenteix quan molta gent els fa servir alhora. Les teves gravacions i les respostes es processen en aquests servidors.\n\nSi el reconeixement local falla o no sent res, aquella gravació s'envia als servidors públics.\n\nSelecciona on es processa la veu, si us plau:", + "content": "L'Open Voice OS converteix el que dius en text (reconeixement de veu) i llegeix les respostes en veu alta (síntesi de veu). Aquest equip és prou potent per fer totes dues coses ell mateix.\n\n- local: tot s'executa en aquest equip. La teva veu es queda aquí i res no depèn d'internet ni de com d'ocupats estiguin els servidors. L'instal·lador baixa els models de veu del teu idioma, des d'uns quants centenars de megabytes fins a uns quants gigabytes, i el reconeixement manté ocupat el processador mentre parles.\n- public: la feina la fan servidors de la comunitat. S'ofereixen de bona voluntat, com a reserva i com a exemple de veu autoallotjada, no com un servei de producció: les respostes triguen més quan molta gent els fa servir alhora, i poden deixar de funcionar en qualsevol moment. No cal baixar res, però les teves gravacions i les respostes viatgen per internet i es processen en aquests servidors.\n\nSi el reconeixement local falla o no sent res, aquella gravació s'envia als servidors públics.\n\nSelecciona on es processa la veu, si us plau:", "local_description": "Processar la veu en aquest equip", - "public_description": "Servidors públics de l'Open Voice OS" + "public_description": "Servidors públics (sé que poden caure)" }, "summary.sh": { "title": "Instal·lació de l'Open VoiceOS - Resum", diff --git a/translations/da/strings.json b/translations/da/strings.json index af552ac7..3d5afac7 100644 --- a/translations/da/strings.json +++ b/translations/da/strings.json @@ -58,9 +58,9 @@ }, "speech.sh": { "title": "Open Voice OS Installation - Tale", - "content": "Open Voice OS omsætter det, du siger, til tekst (talegenkendelse) og læser sine svar højt (talesyntese). Denne maskine er kraftig nok til selv at klare begge dele.\n\n- local: alt kører på denne maskine. Din stemme bliver her, og intet afhænger af internettet eller af, hvor travlt serverne har. Installationsprogrammet henter talemodellerne til dit sprog, fra et par hundrede megabyte til et par gigabyte, og genkendelsen holder processoren beskæftiget, mens du taler.\n- public: de offentlige Open Voice OS-servere klarer arbejdet. Der skal ikke hentes noget, men hver forespørgsel går over internettet til servere, som hele fællesskabet deler. Det giver netværksforsinkelse og bliver langsommere, når mange bruger dem på samme tid. Dine optagelser og svarene behandles på de servere.\n\nHvis den lokale genkendelse fejler eller ikke hører noget, sendes netop den optagelse til de offentlige servere i stedet.\n\nVælg venligst, hvor tale skal behandles:", + "content": "Open Voice OS omsætter det, du siger, til tekst (talegenkendelse) og læser sine svar højt (talesyntese). Denne maskine er kraftig nok til selv at klare begge dele.\n\n- local: alt kører på denne maskine. Din stemme bliver her, og intet afhænger af internettet eller af, hvor travlt serverne har. Installationsprogrammet henter talemodellerne til dit sprog, fra et par hundrede megabyte til et par gigabyte, og genkendelsen holder processoren beskæftiget, mens du taler.\n- public: fællesskabets servere klarer arbejdet. De drives af god vilje, som reserve og som eksempel på selvhostet talebehandling, ikke som en produktionstjeneste: svarene bliver langsommere, når mange bruger dem på samme tid, og serverne kan gå ned når som helst. Der skal ikke hentes noget, men dine optagelser og svarene sendes over internettet og behandles på de servere.\n\nHvis den lokale genkendelse fejler eller ikke hører noget, sendes netop den optagelse til de offentlige servere i stedet.\n\nVælg venligst, hvor tale skal behandles:", "local_description": "Behandl tale på denne maskine", - "public_description": "Brug de offentlige Open Voice OS-servere" + "public_description": "Offentlige servere (jeg ved, de kan gå ned)" }, "summary.sh": { "title": "Open Voice OS Installation - Resume", diff --git a/translations/de-de/strings.json b/translations/de-de/strings.json index a0af879b..b5c48e09 100644 --- a/translations/de-de/strings.json +++ b/translations/de-de/strings.json @@ -58,9 +58,9 @@ }, "speech.sh": { "title": "Open Voice OS-Installation - Sprachverarbeitung", - "content": "Open Voice OS wandelt Gesprochenes in Text um (Spracherkennung) und liest seine Antworten vor (Sprachsynthese). Dieser Rechner ist leistungsfähig genug, beides selbst zu erledigen.\n\n- local: Alles läuft auf diesem Rechner. Ihre Stimme bleibt hier, und nichts hängt vom Internet oder von der Auslastung der Server ab. Der Installer lädt die Sprachmodelle für Ihre Sprache herunter, von einigen hundert Megabyte bis zu ein paar Gigabyte, und die Erkennung lastet den Prozessor aus, während Sie sprechen.\n- public: Die öffentlichen Open Voice OS-Server übernehmen die Arbeit. Es muss nichts heruntergeladen werden, aber jede Anfrage geht über das Internet an Server, die sich die ganze Community teilt. Das kostet Netzwerklaufzeit und wird langsamer, wenn viele sie gleichzeitig nutzen. Ihre Aufnahmen und die Antworten werden auf diesen Servern verarbeitet.\n\nWenn die lokale Erkennung fehlschlägt oder nichts hört, wird genau diese Aufnahme stattdessen an die öffentlichen Server geschickt.\n\nBitte wählen Sie, wo die Sprache verarbeitet wird:", + "content": "Open Voice OS wandelt Gesprochenes in Text um (Spracherkennung) und liest seine Antworten vor (Sprachsynthese). Dieser Rechner ist leistungsfähig genug, beides selbst zu erledigen.\n\n- local: Alles läuft auf diesem Rechner. Ihre Stimme bleibt hier, und nichts hängt vom Internet oder von der Auslastung der Server ab. Der Installer lädt die Sprachmodelle für Ihre Sprache herunter, von einigen hundert Megabyte bis zu ein paar Gigabyte, und die Erkennung lastet den Prozessor aus, während Sie sprechen.\n- public: Server der Community übernehmen die Arbeit. Sie werden aus gutem Willen betrieben, als Rückfallebene und als Beispiel für selbst gehostete Sprachverarbeitung, nicht als Produktionsdienst: Antworten dauern länger, wenn viele sie gleichzeitig nutzen, und die Server können jederzeit ausfallen. Es muss nichts heruntergeladen werden, aber Ihre Aufnahmen und die Antworten gehen über das Internet und werden auf diesen Servern verarbeitet.\n\nWenn die lokale Erkennung fehlschlägt oder nichts hört, wird genau diese Aufnahme stattdessen an die öffentlichen Server geschickt.\n\nBitte wählen Sie, wo die Sprache verarbeitet wird:", "local_description": "Sprache auf diesem Rechner verarbeiten", - "public_description": "Öffentliche Open Voice OS-Server nutzen" + "public_description": "Öffentliche Server (ich weiß, sie können ausfallen)" }, "summary.sh": { "title": "Open Voice OS Installation - Zusammenfassung", diff --git a/translations/en-us/strings.json b/translations/en-us/strings.json index c738c034..2f2d9afd 100644 --- a/translations/en-us/strings.json +++ b/translations/en-us/strings.json @@ -58,9 +58,9 @@ }, "speech.sh": { "title": "Open Voice OS Installation - Speech", - "content": "Open Voice OS turns what you say into text (speech recognition) and reads its answers out loud (speech synthesis). This machine is powerful enough to do both itself.\n\n - local: everything runs on this machine. Your voice stays here, and nothing depends on the internet or on how busy the servers are. The installer downloads the speech models for your language, from a few hundred megabytes to a few gigabytes, and recognition keeps the processor busy while you talk.\n - public: the Open Voice OS public servers do the work. Nothing to download, but every request travels over the internet to servers the whole community shares, which adds network delay and slows down when many people use them at once. Your recordings and the answers are processed on those servers.\n\nWhen local recognition fails or hears nothing, that one recording is sent to the public servers instead.\n\nPlease select where speech is processed:", + "content": "Open Voice OS turns what you say into text (speech recognition) and reads its answers out loud (speech synthesis). This machine is powerful enough to do both itself.\n\n - local: everything runs on this machine. Your voice stays here, and nothing depends on the internet or on how busy the servers are. The installer downloads the speech models for your language, from a few hundred megabytes to a few gigabytes, and recognition keeps the processor busy while you talk.\n - public: community servers do the work. They run on goodwill, as a backup and as an example of self-hosted speech, not as a production service: answers take longer when many people use them, and the servers can go offline at any time. Nothing to download, but your recordings and the answers travel over the internet and are processed on those servers.\n\nWhen local recognition fails or hears nothing, that one recording is sent to the public servers instead.\n\nPlease select where speech is processed:", "local_description": "Run speech on this machine", - "public_description": "Use the Open Voice OS public servers" + "public_description": "Public servers (I accept they can go offline)" }, "summary.sh": { "title": "Open Voice OS Installation - Summary", diff --git a/translations/es-es/strings.json b/translations/es-es/strings.json index 8defb709..82f8519b 100644 --- a/translations/es-es/strings.json +++ b/translations/es-es/strings.json @@ -58,9 +58,9 @@ }, "speech.sh": { "title": "Instalación de Open Voice OS - Voz", - "content": "Open Voice OS convierte lo que dice en texto (reconocimiento de voz) y lee sus respuestas en voz alta (síntesis de voz). Este equipo es lo bastante potente para hacer ambas cosas por sí mismo.\n\n- local: todo se ejecuta en este equipo. Su voz se queda aquí y nada depende de internet ni de lo ocupados que estén los servidores. El instalador descarga los modelos de voz de su idioma, desde unos cientos de megabytes hasta unos pocos gigabytes, y el reconocimiento mantiene ocupado el procesador mientras habla.\n- public: los servidores públicos de Open Voice OS hacen el trabajo. No hay nada que descargar, pero cada petición viaja por internet hasta servidores que comparte toda la comunidad, lo que añade retardo de red y se vuelve más lento cuando muchas personas los usan a la vez. Sus grabaciones y las respuestas se procesan en esos servidores.\n\nSi el reconocimiento local falla o no oye nada, esa grabación se envía a los servidores públicos.\n\nSeleccione dónde se procesa la voz:", + "content": "Open Voice OS convierte lo que dice en texto (reconocimiento de voz) y lee sus respuestas en voz alta (síntesis de voz). Este equipo es lo bastante potente para hacer ambas cosas por sí mismo.\n\n- local: todo se ejecuta en este equipo. Su voz se queda aquí y nada depende de internet ni de lo ocupados que estén los servidores. El instalador descarga los modelos de voz de su idioma, desde unos cientos de megabytes hasta unos pocos gigabytes, y el reconocimiento mantiene ocupado el procesador mientras habla.\n- public: el trabajo lo hacen servidores de la comunidad. Se ofrecen de buena voluntad, como respaldo y como ejemplo de voz autoalojada, no como un servicio de producción: las respuestas tardan más cuando mucha gente los usa a la vez, y pueden dejar de funcionar en cualquier momento. No hay nada que descargar, pero sus grabaciones y las respuestas viajan por internet y se procesan en esos servidores.\n\nSi el reconocimiento local falla o no oye nada, esa grabación se envía a los servidores públicos.\n\nSeleccione dónde se procesa la voz:", "local_description": "Procesar la voz en este equipo", - "public_description": "Usar los servidores públicos de Open Voice OS" + "public_description": "Servidores públicos (sé que pueden caerse)" }, "summary.sh": { "title": "Instalación de Open Voice OS - Resumen", diff --git a/translations/eu-es/strings.json b/translations/eu-es/strings.json index 01084136..7017cc99 100644 --- a/translations/eu-es/strings.json +++ b/translations/eu-es/strings.json @@ -57,9 +57,9 @@ }, "speech.sh": { "title": "Open Voice OS instalazioa - Ahotsa", - "content": "Open Voice OS-k esaten duzuna testu bihurtzen du (ahots-ezagutza) eta bere erantzunak ozen irakurtzen ditu (ahots-sintesia). Makina honek bi lanak berak egiteko adina indar du.\n\n - local: dena makina honetan exekutatzen da. Zure ahotsa hemen geratzen da, eta ezer ez dago Interneten edo zerbitzarien lan-kargaren mende. Instalatzaileak zure hizkuntzako ahots-ereduak deskargatzen ditu, ehunka megabytetik gigabyte gutxi batzuetara, eta ezagutzak prozesadorea lanpetuta mantentzen du hitz egiten duzun bitartean.\n - public: Open Voice OS-en zerbitzari publikoek egiten dute lana. Ez dago ezer deskargatu beharrik, baina eskaera bakoitza Internet bidez komunitate osoak partekatzen dituen zerbitzarietara doa. Horrek sareko atzerapena gehitzen du, eta motelago doa jende askok aldi berean erabiltzen dituenean. Zure grabazioak eta erantzunak zerbitzari horietan prozesatzen dira.\n\nEzagutza lokalak huts egiten badu edo ezer entzuten ez badu, grabazio hori zerbitzari publikoetara bidaltzen da.\n\nAukeratu non prozesatzen den ahotsa:", + "content": "Open Voice OS-k esaten duzuna testu bihurtzen du (ahots-ezagutza) eta bere erantzunak ozen irakurtzen ditu (ahots-sintesia). Makina honek bi lanak berak egiteko adina indar du.\n\n - local: dena makina honetan exekutatzen da. Zure ahotsa hemen geratzen da, eta ezer ez dago Interneten edo zerbitzarien lan-kargaren mende. Instalatzaileak zure hizkuntzako ahots-ereduak deskargatzen ditu, ehunka megabytetik gigabyte gutxi batzuetara, eta ezagutzak prozesadorea lanpetuta mantentzen du hitz egiten duzun bitartean.\n - public: komunitatearen zerbitzariek egiten dute lana. Borondate onez eskaintzen dira, babes gisa eta norberak ostatatutako ahotsaren adibide gisa, ez produkzio-zerbitzu gisa: erantzunak motelago iristen dira jende askok aldi berean erabiltzen dituenean, eta edozein unetan gelditu daitezke. Ez dago ezer deskargatu beharrik, baina zure grabazioak eta erantzunak Internet bidez doaz eta zerbitzari horietan prozesatzen dira.\n\nEzagutza lokalak huts egiten badu edo ezer entzuten ez badu, grabazio hori zerbitzari publikoetara bidaltzen da.\n\nAukeratu non prozesatzen den ahotsa:", "local_description": "Prozesatu ahotsa makina honetan", - "public_description": "Erabili Open Voice OS-en zerbitzari publikoak" + "public_description": "Zerbitzari publikoak (badakit eror daitezkeela)" }, "summary.sh": { "title": "Ireki Voice OS instalazioa - Laburpena", diff --git a/translations/fr-fr/strings.json b/translations/fr-fr/strings.json index adab6472..ae9ca07e 100644 --- a/translations/fr-fr/strings.json +++ b/translations/fr-fr/strings.json @@ -58,9 +58,9 @@ }, "speech.sh": { "title": "Open Voice OS Installation - Traitement de la parole", - "content": "Open Voice OS transforme ce que vous dites en texte (reconnaissance vocale) et lit ses réponses à voix haute (synthèse vocale). Cette machine est assez puissante pour faire les deux elle-même.\n\n- local : tout fonctionne sur cette machine. Votre voix reste ici, et rien ne dépend d'Internet ni de la charge des serveurs. L'installateur télécharge les modèles vocaux de votre langue, de quelques centaines de mégaoctets à quelques gigaoctets, et la reconnaissance occupe le processeur pendant que vous parlez.\n- public : les serveurs publics d'Open Voice OS font le travail. Rien à télécharger, mais chaque requête passe par Internet vers des serveurs partagés par toute la communauté, ce qui ajoute du délai réseau et ralentit quand beaucoup de monde les utilise en même temps. Vos enregistrements et les réponses sont traités sur ces serveurs.\n\nSi la reconnaissance locale échoue ou n'entend rien, cet enregistrement-là est envoyé aux serveurs publics à la place.\n\nVeuillez sélectionner où la parole est traitée :", + "content": "Open Voice OS transforme ce que vous dites en texte (reconnaissance vocale) et lit ses réponses à voix haute (synthèse vocale). Cette machine est assez puissante pour faire les deux elle-même.\n\n- local : tout fonctionne sur cette machine. Votre voix reste ici, et rien ne dépend d'Internet ni de la charge des serveurs. L'installateur télécharge les modèles vocaux de votre langue, de quelques centaines de mégaoctets à quelques gigaoctets, et la reconnaissance occupe le processeur pendant que vous parlez.\n- public : des serveurs communautaires font le travail. Ils sont fournis de bonne volonté, comme solution de secours et comme exemple de traitement de la parole auto-hébergé, pas comme un service de production : les réponses ralentissent quand beaucoup de monde les utilise, et ils peuvent s'arrêter à tout moment. Rien à télécharger, mais vos enregistrements et les réponses passent par Internet et sont traités sur ces serveurs.\n\nSi la reconnaissance locale échoue ou n'entend rien, cet enregistrement-là est envoyé aux serveurs publics à la place.\n\nVeuillez sélectionner où la parole est traitée :", "local_description": "Traiter la parole sur cette machine", - "public_description": "Utiliser les serveurs publics d'Open Voice OS" + "public_description": "Serveurs publics (je sais qu'ils peuvent tomber)" }, "summary.sh": { "title": "Open Voice OS Installation - Résumé", diff --git a/translations/gl-es/strings.json b/translations/gl-es/strings.json index 88221344..0c0b1594 100644 --- a/translations/gl-es/strings.json +++ b/translations/gl-es/strings.json @@ -58,9 +58,9 @@ }, "speech.sh": { "title": "Instalación de Open Voice OS - Voz", - "content": "Open Voice OS converte o que dis en texto (recoñecemento de voz) e le as súas respostas en voz alta (síntese de voz). Este equipo é abondo potente para facer as dúas cousas el mesmo.\n\n - local: todo se executa neste equipo. A túa voz queda aquí e nada depende de internet nin de canto de ocupados estean os servidores. O instalador descarga os modelos de voz do teu idioma, desde uns centos de megabytes ata uns poucos gigabytes, e o recoñecemento mantén ocupado o procesador mentres falas.\n - public: os servidores públicos de Open Voice OS fan o traballo. Non hai nada que descargar, pero cada petición viaxa por internet ata servidores que comparte toda a comunidade, o que engade atraso de rede e vólvese máis lento cando moita xente os usa á vez. As túas gravacións e as respostas procésanse neses servidores.\n\nSe o recoñecemento local falla ou non oe nada, esa gravación envíase aos servidores públicos.\n\nSelecciona onde se procesa a voz:", + "content": "Open Voice OS converte o que dis en texto (recoñecemento de voz) e le as súas respostas en voz alta (síntese de voz). Este equipo é abondo potente para facer as dúas cousas el mesmo.\n\n - local: todo se executa neste equipo. A túa voz queda aquí e nada depende de internet nin de canto de ocupados estean os servidores. O instalador descarga os modelos de voz do teu idioma, desde uns centos de megabytes ata uns poucos gigabytes, e o recoñecemento mantén ocupado o procesador mentres falas.\n - public: o traballo fano servidores da comunidade. Ofrécense de boa vontade, como respaldo e como exemplo de voz autoaloxada, non como un servizo de produción: as respostas tardan máis cando moita xente os usa á vez, e poden deixar de funcionar en calquera momento. Non hai nada que descargar, pero as túas gravacións e as respostas viaxan por internet e procésanse neses servidores.\n\nSe o recoñecemento local falla ou non oe nada, esa gravación envíase aos servidores públicos.\n\nSelecciona onde se procesa a voz:", "local_description": "Procesar a voz neste equipo", - "public_description": "Usar os servidores públicos de Open Voice OS" + "public_description": "Servidores públicos (sei que poden caer)" }, "summary.sh": { "title": "Instalación de Open Voice OS - Recapitulación", diff --git a/translations/hi-in/strings.json b/translations/hi-in/strings.json index 6650ccb8..8f3dfcb5 100644 --- a/translations/hi-in/strings.json +++ b/translations/hi-in/strings.json @@ -57,9 +57,9 @@ }, "speech.sh": { "title": "Open Voice OS Installation - आवाज़", - "content": "Open Voice OS आपकी कही बात को टेक्स्ट में बदलता है (वाक् पहचान) और अपने जवाब बोलकर सुनाता है (वाक् संश्लेषण)। यह मशीन ये दोनों काम ख़ुद करने के लिए काफ़ी शक्तिशाली है।\n\n - local: सब कुछ इसी मशीन पर चलता है। आपकी आवाज़ यहीं रहती है, और कुछ भी इंटरनेट या सर्वरों की व्यस्तता पर निर्भर नहीं करता। इंस्टॉलर आपकी भाषा के वाक् मॉडल डाउनलोड करता है, जो कुछ सौ मेगाबाइट से लेकर कुछ गीगाबाइट तक के होते हैं, और आपके बोलते समय पहचान प्रोसेसर को व्यस्त रखती है।\n - public: Open Voice OS के सार्वजनिक सर्वर यह काम करते हैं। कुछ डाउनलोड नहीं करना पड़ता, लेकिन हर अनुरोध इंटरनेट से होकर उन सर्वरों तक जाता है जिन्हें पूरा समुदाय साझा करता है। इससे नेटवर्क की देरी जुड़ती है, और जब बहुत से लोग एक साथ इनका उपयोग करते हैं तो जवाब धीमे हो जाते हैं। आपकी रिकॉर्डिंग और जवाब इन्हीं सर्वरों पर प्रोसेस होते हैं।\n\nअगर लोकल पहचान विफल हो जाए या कुछ न सुन पाए, तो वह रिकॉर्डिंग सार्वजनिक सर्वरों पर भेज दी जाती है।\n\nकृपया चुनें कि आवाज़ कहाँ प्रोसेस हो:", + "content": "Open Voice OS आपकी कही बात को टेक्स्ट में बदलता है (वाक् पहचान) और अपने जवाब बोलकर सुनाता है (वाक् संश्लेषण)। यह मशीन ये दोनों काम ख़ुद करने के लिए काफ़ी शक्तिशाली है।\n\n - local: सब कुछ इसी मशीन पर चलता है। आपकी आवाज़ यहीं रहती है, और कुछ भी इंटरनेट या सर्वरों की व्यस्तता पर निर्भर नहीं करता। इंस्टॉलर आपकी भाषा के वाक् मॉडल डाउनलोड करता है, जो कुछ सौ मेगाबाइट से लेकर कुछ गीगाबाइट तक के होते हैं, और आपके बोलते समय पहचान प्रोसेसर को व्यस्त रखती है।\n - public: समुदाय के सर्वर यह काम करते हैं। इन्हें सद्भावना से चलाया जाता है, बैकअप के तौर पर और सेल्फ़-होस्टेड वाक् प्रसंस्करण के उदाहरण के रूप में, किसी प्रोडक्शन सेवा की तरह नहीं: जब बहुत से लोग एक साथ इनका उपयोग करते हैं तो जवाब धीमे हो जाते हैं, और ये सर्वर कभी भी बंद हो सकते हैं। कुछ डाउनलोड नहीं करना पड़ता, लेकिन आपकी रिकॉर्डिंग और जवाब इंटरनेट से होकर इन्हीं सर्वरों पर प्रोसेस होते हैं।\n\nअगर लोकल पहचान विफल हो जाए या कुछ न सुन पाए, तो वह रिकॉर्डिंग सार्वजनिक सर्वरों पर भेज दी जाती है।\n\nकृपया चुनें कि आवाज़ कहाँ प्रोसेस हो:", "local_description": "इसी मशीन पर आवाज़ प्रोसेस करें", - "public_description": "Open Voice OS के सार्वजनिक सर्वर इस्तेमाल करें" + "public_description": "सार्वजनिक सर्वर (ये कभी भी बंद हो सकते हैं)" }, "summary.sh": { "title": "Open Voice OS Installation - सारांश", diff --git a/translations/it-it/strings.json b/translations/it-it/strings.json index 4c04088e..d1a2dd16 100644 --- a/translations/it-it/strings.json +++ b/translations/it-it/strings.json @@ -58,9 +58,9 @@ }, "speech.sh": { "title": "Installazione di Open Voice OS - Voce", - "content": "Open Voice OS trasforma ciò che dici in testo (riconoscimento vocale) e legge le risposte ad alta voce (sintesi vocale). Questa macchina è abbastanza potente da fare entrambe le cose da sola.\n\n - local: tutto gira su questa macchina. La tua voce resta qui e nulla dipende da internet o da quanto sono occupati i server. L'installer scarica i modelli vocali per la tua lingua, da qualche centinaio di megabyte a qualche gigabyte, e il riconoscimento tiene occupato il processore mentre parli.\n - public: il lavoro lo fanno i server pubblici di Open Voice OS. Niente da scaricare, ma ogni richiesta viaggia su internet verso server condivisi da tutta la comunità: questo aggiunge ritardo di rete e rallenta quando molte persone li usano contemporaneamente. Le tue registrazioni e le risposte vengono elaborate su quei server.\n\nSe il riconoscimento locale fallisce o non sente nulla, quella registrazione viene inviata ai server pubblici.\n\nSeleziona dove elaborare la voce:", + "content": "Open Voice OS trasforma ciò che dici in testo (riconoscimento vocale) e legge le risposte ad alta voce (sintesi vocale). Questa macchina è abbastanza potente da fare entrambe le cose da sola.\n\n - local: tutto gira su questa macchina. La tua voce resta qui e nulla dipende da internet o da quanto sono occupati i server. L'installer scarica i modelli vocali per la tua lingua, da qualche centinaio di megabyte a qualche gigabyte, e il riconoscimento tiene occupato il processore mentre parli.\n - public: il lavoro lo fanno server della comunità. Sono offerti per buona volontà, come riserva e come esempio di voce in self-hosting, non come servizio di produzione: le risposte rallentano quando molte persone li usano contemporaneamente, e possono andare offline in qualsiasi momento. Niente da scaricare, ma le tue registrazioni e le risposte viaggiano su internet e vengono elaborate su quei server.\n\nSe il riconoscimento locale fallisce o non sente nulla, quella registrazione viene inviata ai server pubblici.\n\nSeleziona dove elaborare la voce:", "local_description": "Elaborare la voce su questa macchina", - "public_description": "Usare i server pubblici di Open Voice OS" + "public_description": "Server pubblici (so che possono andare offline)" }, "summary.sh": { "title": "Installazione di Open Voice OS - Riassunto", diff --git a/translations/kab-dz/strings.json b/translations/kab-dz/strings.json index 50edfb93..543c92d8 100644 --- a/translations/kab-dz/strings.json +++ b/translations/kab-dz/strings.json @@ -56,9 +56,9 @@ }, "speech.sh": { "title": "Asbeddi n Open Voice OS - Taɣect", - "content": "Open Voice OS yettbeddil ayen i d-tenniḍ ɣer uḍris (aɛqal n taɣect) yerna yeqqar-d tiririyin-is s taɣect (asuddes n taɣect). Tamacint-a tezmer ad txeddem snat-a s yiman-is.\n\n - local: kullec ad iteddu ɣef tmacint-a. Taɣect-ik ad teqqim da, ulac ayen icudden ɣer Internet neɣ ɣer yiqeddacen. Amesbeddi ad d-yessader timudmin n taɣect n tutlayt-ik, seg kra n yimiḍ n MB alamma d kra n GB, yerna aɛqal ad yesseqdec amesbadu mi ara tettmeslayeḍ.\n - public: d iqeddacen izayazen n Open Voice OS ara ixedmen axeddim. Ulac ayen ara d-tessadreḍ, maca yal asuter ad yeddu s Internet ɣer yiqeddacen i tebḍa merra temɣiwant: aya yettarra-d aɛettel, yerna ad ẓẓayen mi ara ten-sqedcen aṭas n medden ɣef tikkelt. Iseklasen-ik d tririyin ad ttwasenfaren ɣef yiqeddacen-a.\n\nMa yecceḍ uɛqal adigan neɣ ur yesla i kra, asekles-nni ad yettwazen ɣer yiqeddacen izayazen.\n\nTtxil-k fren anida ara tettwasenfar taɣect:", + "content": "Open Voice OS yettbeddil ayen i d-tenniḍ ɣer uḍris (aɛqal n taɣect) yerna yeqqar-d tiririyin-is s taɣect (asuddes n taɣect). Tamacint-a tezmer ad txeddem snat-a s yiman-is.\n\n - local: kullec ad iteddu ɣef tmacint-a. Taɣect-ik ad teqqim da, ulac ayen icudden ɣer Internet neɣ ɣer yiqeddacen. Amesbeddi ad d-yessader timudmin n taɣect n tutlayt-ik, seg kra n yimiḍ n MB alamma d kra n GB, yerna aɛqal ad yesseqdec amesbadu mi ara tettmeslayeḍ.\n - public: d iqeddacen n temɣiwant ara ixedmen axeddim. Ttwafken-d s lebɣi, am tarrayt n uɛiwen d umedya n taɣect i yesbedden yiman-is, maci am umeẓlu n tfalit: tiririyin ad ẓẓayent mi ara ten-sqedcen aṭas n medden ɣef tikkelt, yerna zemren ad ḥbesen melmi tebɣu tili. Ulac ayen ara d-tessadreḍ, maca iseklasen-ik d tririyin ad ɛeddin s Internet u ad ttwasenfaren ɣef yiqeddacen-a.\n\nMa yecceḍ uɛqal adigan neɣ ur yesla i kra, asekles-nni ad yettwazen ɣer yiqeddacen izayazen.\n\nTtxil-k fren anida ara tettwasenfar taɣect:", "local_description": "Taɣect ɣef tmacint-a", - "public_description": "Iqeddacen izayazen n Open Voice OS" + "public_description": "Iqeddacen izayazen (ẓriɣ zemren ad ḥbesen)" }, "summary.sh": { "title": "Asbeddi n Open Voice OS - Agzul", diff --git a/translations/nl-nl/strings.json b/translations/nl-nl/strings.json index b3702f94..b6caf68d 100644 --- a/translations/nl-nl/strings.json +++ b/translations/nl-nl/strings.json @@ -58,9 +58,9 @@ }, "speech.sh": { "title": "Open Voice OS Installatie - Spraak", - "content": "Open Voice OS zet wat je zegt om in tekst (spraakherkenning) en leest zijn antwoorden voor (spraaksynthese). Deze machine is krachtig genoeg om beide zelf te doen.\n\n- local: alles draait op deze machine. Je stem blijft hier, en niets hangt af van internet of van hoe druk de servers zijn. Het installatieprogramma downloadt de spraakmodellen voor je taal, van een paar honderd megabyte tot een paar gigabyte, en de herkenning houdt de processor bezig terwijl je praat.\n- public: de openbare servers van Open Voice OS doen het werk. Je hoeft niets te downloaden, maar elk verzoek gaat via internet naar servers die de hele community deelt. Dat voegt netwerkvertraging toe en wordt trager wanneer veel mensen ze tegelijk gebruiken. Je opnames en de antwoorden worden op die servers verwerkt.\n\nAls de lokale herkenning mislukt of niets hoort, wordt die ene opname naar de openbare servers gestuurd.\n\nSelecteer waar spraak wordt verwerkt:", + "content": "Open Voice OS zet wat je zegt om in tekst (spraakherkenning) en leest zijn antwoorden voor (spraaksynthese). Deze machine is krachtig genoeg om beide zelf te doen.\n\n- local: alles draait op deze machine. Je stem blijft hier, en niets hangt af van internet of van hoe druk de servers zijn. Het installatieprogramma downloadt de spraakmodellen voor je taal, van een paar honderd megabyte tot een paar gigabyte, en de herkenning houdt de processor bezig terwijl je praat.\n- public: servers van de community doen het werk. Ze draaien uit goede wil, als reserve en als voorbeeld van zelf gehoste spraakverwerking, niet als productiedienst: antwoorden duren langer wanneer veel mensen ze tegelijk gebruiken, en de servers kunnen op elk moment uitvallen. Je hoeft niets te downloaden, maar je opnames en de antwoorden gaan via internet en worden op die servers verwerkt.\n\nAls de lokale herkenning mislukt of niets hoort, wordt die ene opname naar de openbare servers gestuurd.\n\nSelecteer waar spraak wordt verwerkt:", "local_description": "Spraak op deze machine verwerken", - "public_description": "De openbare Open Voice OS-servers gebruiken" + "public_description": "Openbare servers (ik weet dat ze kunnen uitvallen)" }, "summary.sh": { "title": "OpenVoice OS Installatie - Samenvatting", diff --git a/translations/pl-pl/strings.json b/translations/pl-pl/strings.json index 27d0c43f..8f7532cf 100644 --- a/translations/pl-pl/strings.json +++ b/translations/pl-pl/strings.json @@ -58,9 +58,9 @@ }, "speech.sh": { "title": "Instalacja Open Voice OS - Mowa", - "content": "Open Voice OS zamienia to, co mówisz, na tekst (rozpoznawanie mowy) i czyta swoje odpowiedzi na głos (synteza mowy). Ten komputer jest na tyle wydajny, żeby robić jedno i drugie samodzielnie.\n\n- local: wszystko działa na tym komputerze. Twój głos zostaje tutaj i nic nie zależy od internetu ani od obciążenia serwerów. Instalator pobiera modele mowy dla Twojego języka, od kilkuset megabajtów do kilku gigabajtów, a rozpoznawanie obciąża procesor, gdy mówisz.\n- public: pracę wykonują publiczne serwery Open Voice OS. Nic nie trzeba pobierać, ale każde żądanie wędruje przez internet do serwerów współdzielonych przez całą społeczność, co dodaje opóźnienie sieciowe i spowalnia działanie, gdy korzysta z nich wiele osób naraz. Twoje nagrania i odpowiedzi są przetwarzane na tych serwerach.\n\nJeśli lokalne rozpoznawanie zawiedzie lub nic nie usłyszy, to nagranie zostanie wysłane do serwerów publicznych.\n\nWybierz, gdzie ma być przetwarzana mowa:", + "content": "Open Voice OS zamienia to, co mówisz, na tekst (rozpoznawanie mowy) i czyta swoje odpowiedzi na głos (synteza mowy). Ten komputer jest na tyle wydajny, żeby robić jedno i drugie samodzielnie.\n\n- local: wszystko działa na tym komputerze. Twój głos zostaje tutaj i nic nie zależy od internetu ani od obciążenia serwerów. Instalator pobiera modele mowy dla Twojego języka, od kilkuset megabajtów do kilku gigabajtów, a rozpoznawanie obciąża procesor, gdy mówisz.\n- public: pracę wykonują serwery społeczności. Działają w dobrej wierze, jako zapas i przykład samodzielnie hostowanego przetwarzania mowy, a nie jako usługa produkcyjna: odpowiedzi zwalniają, gdy korzysta z nich wiele osób naraz, a serwery w każdej chwili mogą przestać działać. Nic nie trzeba pobierać, ale Twoje nagrania i odpowiedzi trafiają przez internet na te serwery i tam są przetwarzane.\n\nJeśli lokalne rozpoznawanie zawiedzie lub nic nie usłyszy, to nagranie zostanie wysłane do serwerów publicznych.\n\nWybierz, gdzie ma być przetwarzana mowa:", "local_description": "Przetwarzaj mowę na tym komputerze", - "public_description": "Używaj publicznych serwerów Open Voice OS" + "public_description": "Serwery publiczne (wiem, że mogą przestać działać)" }, "summary.sh": { "title": "Instalacja Open Voice OS – Podsumowanie", diff --git a/translations/pt-pt/strings.json b/translations/pt-pt/strings.json index 186289cc..cd52485a 100644 --- a/translations/pt-pt/strings.json +++ b/translations/pt-pt/strings.json @@ -58,9 +58,9 @@ }, "speech.sh": { "title": "Open Voice OS Instalação - Voz", - "content": "O Open Voice OS converte o que diz em texto (reconhecimento de voz) e lê as respostas em voz alta (síntese de voz). Esta máquina é suficientemente potente para fazer as duas coisas sozinha.\n\n - local: tudo corre nesta máquina. A sua voz fica aqui e nada depende da internet nem da ocupação dos servidores. O instalador descarrega os modelos de voz do seu idioma, de algumas centenas de megabytes a alguns gigabytes, e o reconhecimento mantém o processador ocupado enquanto fala.\n - public: os servidores públicos do Open Voice OS fazem o trabalho. Não há nada para descarregar, mas cada pedido viaja pela internet até servidores partilhados por toda a comunidade, o que acrescenta atraso de rede e fica mais lento quando muitas pessoas os usam ao mesmo tempo. As suas gravações e as respostas são processadas nesses servidores.\n\nSe o reconhecimento local falhar ou não ouvir nada, essa gravação é enviada para os servidores públicos.\n\nSeleccione onde a voz é processada:", + "content": "O Open Voice OS converte o que diz em texto (reconhecimento de voz) e lê as respostas em voz alta (síntese de voz). Esta máquina é suficientemente potente para fazer as duas coisas sozinha.\n\n - local: tudo corre nesta máquina. A sua voz fica aqui e nada depende da internet nem da ocupação dos servidores. O instalador descarrega os modelos de voz do seu idioma, de algumas centenas de megabytes a alguns gigabytes, e o reconhecimento mantém o processador ocupado enquanto fala.\n - public: o trabalho é feito por servidores da comunidade. São disponibilizados por boa vontade, como reserva e como exemplo de voz auto-alojada, não como um serviço de produção: as respostas demoram mais quando muitas pessoas os usam ao mesmo tempo, e podem ficar indisponíveis a qualquer momento. Não há nada para descarregar, mas as suas gravações e as respostas viajam pela internet e são processadas nesses servidores.\n\nSe o reconhecimento local falhar ou não ouvir nada, essa gravação é enviada para os servidores públicos.\n\nSeleccione onde a voz é processada:", "local_description": "Processar a voz nesta máquina", - "public_description": "Usar os servidores públicos do Open Voice OS" + "public_description": "Servidores públicos (sei que podem falhar)" }, "summary.sh": { "title": "Open Voice OS Instalação - Resumo", diff --git a/tui/locales/ca-es/speech.sh b/tui/locales/ca-es/speech.sh index 546eeda7..d4f09ba6 100644 --- a/tui/locales/ca-es/speech.sh +++ b/tui/locales/ca-es/speech.sh @@ -3,7 +3,7 @@ CONTENT=" L'Open Voice OS converteix el que dius en text (reconeixement de veu) i llegeix les respostes en veu alta (síntesi de veu). Aquest equip és prou potent per fer totes dues coses ell mateix. - local: tot s'executa en aquest equip. La teva veu es queda aquí i res no depèn d'internet ni de com d'ocupats estiguin els servidors. L'instal·lador baixa els models de veu del teu idioma, des d'uns quants centenars de megabytes fins a uns quants gigabytes, i el reconeixement manté ocupat el processador mentre parles. -- public: els servidors públics de l'Open Voice OS fan la feina. No cal baixar res, però cada petició viatja per internet fins a servidors que comparteix tota la comunitat, cosa que hi afegeix retard de xarxa i que s'alenteix quan molta gent els fa servir alhora. Les teves gravacions i les respostes es processen en aquests servidors. +- public: la feina la fan servidors de la comunitat. S'ofereixen de bona voluntat, com a reserva i com a exemple de veu autoallotjada, no com un servei de producció: les respostes triguen més quan molta gent els fa servir alhora, i poden deixar de funcionar en qualsevol moment. No cal baixar res, però les teves gravacions i les respostes viatgen per internet i es processen en aquests servidors. Si el reconeixement local falla o no sent res, aquella gravació s'envia als servidors públics. @@ -11,6 +11,6 @@ Selecciona on es processa la veu, si us plau: " TITLE="Instal·lació de l'Open Voice OS - Veu" LOCAL_DESCRIPTION="Processar la veu en aquest equip" -PUBLIC_DESCRIPTION="Servidors públics de l'Open Voice OS" +PUBLIC_DESCRIPTION="Servidors públics (sé que poden caure)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/da/speech.sh b/tui/locales/da/speech.sh index 12cf7ebc..726b48da 100644 --- a/tui/locales/da/speech.sh +++ b/tui/locales/da/speech.sh @@ -3,7 +3,7 @@ CONTENT=" Open Voice OS omsætter det, du siger, til tekst (talegenkendelse) og læser sine svar højt (talesyntese). Denne maskine er kraftig nok til selv at klare begge dele. - local: alt kører på denne maskine. Din stemme bliver her, og intet afhænger af internettet eller af, hvor travlt serverne har. Installationsprogrammet henter talemodellerne til dit sprog, fra et par hundrede megabyte til et par gigabyte, og genkendelsen holder processoren beskæftiget, mens du taler. -- public: de offentlige Open Voice OS-servere klarer arbejdet. Der skal ikke hentes noget, men hver forespørgsel går over internettet til servere, som hele fællesskabet deler. Det giver netværksforsinkelse og bliver langsommere, når mange bruger dem på samme tid. Dine optagelser og svarene behandles på de servere. +- public: fællesskabets servere klarer arbejdet. De drives af god vilje, som reserve og som eksempel på selvhostet talebehandling, ikke som en produktionstjeneste: svarene bliver langsommere, når mange bruger dem på samme tid, og serverne kan gå ned når som helst. Der skal ikke hentes noget, men dine optagelser og svarene sendes over internettet og behandles på de servere. Hvis den lokale genkendelse fejler eller ikke hører noget, sendes netop den optagelse til de offentlige servere i stedet. @@ -11,6 +11,6 @@ Vælg venligst, hvor tale skal behandles: " TITLE="Open Voice OS Installation - Tale" LOCAL_DESCRIPTION="Behandl tale på denne maskine" -PUBLIC_DESCRIPTION="Brug de offentlige Open Voice OS-servere" +PUBLIC_DESCRIPTION="Offentlige servere (jeg ved, de kan gå ned)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/de-de/speech.sh b/tui/locales/de-de/speech.sh index b0760714..10e2210d 100644 --- a/tui/locales/de-de/speech.sh +++ b/tui/locales/de-de/speech.sh @@ -3,7 +3,7 @@ CONTENT=" Open Voice OS wandelt Gesprochenes in Text um (Spracherkennung) und liest seine Antworten vor (Sprachsynthese). Dieser Rechner ist leistungsfähig genug, beides selbst zu erledigen. - local: Alles läuft auf diesem Rechner. Ihre Stimme bleibt hier, und nichts hängt vom Internet oder von der Auslastung der Server ab. Der Installer lädt die Sprachmodelle für Ihre Sprache herunter, von einigen hundert Megabyte bis zu ein paar Gigabyte, und die Erkennung lastet den Prozessor aus, während Sie sprechen. -- public: Die öffentlichen Open Voice OS-Server übernehmen die Arbeit. Es muss nichts heruntergeladen werden, aber jede Anfrage geht über das Internet an Server, die sich die ganze Community teilt. Das kostet Netzwerklaufzeit und wird langsamer, wenn viele sie gleichzeitig nutzen. Ihre Aufnahmen und die Antworten werden auf diesen Servern verarbeitet. +- public: Server der Community übernehmen die Arbeit. Sie werden aus gutem Willen betrieben, als Rückfallebene und als Beispiel für selbst gehostete Sprachverarbeitung, nicht als Produktionsdienst: Antworten dauern länger, wenn viele sie gleichzeitig nutzen, und die Server können jederzeit ausfallen. Es muss nichts heruntergeladen werden, aber Ihre Aufnahmen und die Antworten gehen über das Internet und werden auf diesen Servern verarbeitet. Wenn die lokale Erkennung fehlschlägt oder nichts hört, wird genau diese Aufnahme stattdessen an die öffentlichen Server geschickt. @@ -11,6 +11,6 @@ Bitte wählen Sie, wo die Sprache verarbeitet wird: " TITLE="Open Voice OS-Installation - Sprachverarbeitung" LOCAL_DESCRIPTION="Sprache auf diesem Rechner verarbeiten" -PUBLIC_DESCRIPTION="Öffentliche Open Voice OS-Server nutzen" +PUBLIC_DESCRIPTION="Öffentliche Server (ich weiß, sie können ausfallen)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/en-us/speech.sh b/tui/locales/en-us/speech.sh index 84b33c84..47fad248 100644 --- a/tui/locales/en-us/speech.sh +++ b/tui/locales/en-us/speech.sh @@ -3,7 +3,7 @@ CONTENT=" Open Voice OS turns what you say into text (speech recognition) and reads its answers out loud (speech synthesis). This machine is powerful enough to do both itself. - local: everything runs on this machine. Your voice stays here, and nothing depends on the internet or on how busy the servers are. The installer downloads the speech models for your language, from a few hundred megabytes to a few gigabytes, and recognition keeps the processor busy while you talk. - - public: the Open Voice OS public servers do the work. Nothing to download, but every request travels over the internet to servers the whole community shares, which adds network delay and slows down when many people use them at once. Your recordings and the answers are processed on those servers. + - public: community servers do the work. They run on goodwill, as a backup and as an example of self-hosted speech, not as a production service: answers take longer when many people use them, and the servers can go offline at any time. Nothing to download, but your recordings and the answers travel over the internet and are processed on those servers. When local recognition fails or hears nothing, that one recording is sent to the public servers instead. @@ -11,6 +11,6 @@ Please select where speech is processed: " TITLE="Open Voice OS Installation - Speech" LOCAL_DESCRIPTION="Run speech on this machine" -PUBLIC_DESCRIPTION="Use the Open Voice OS public servers" +PUBLIC_DESCRIPTION="Public servers (I accept they can go offline)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/es-es/speech.sh b/tui/locales/es-es/speech.sh index a8f66e18..cf7ddfb2 100644 --- a/tui/locales/es-es/speech.sh +++ b/tui/locales/es-es/speech.sh @@ -3,7 +3,7 @@ CONTENT=" Open Voice OS convierte lo que dice en texto (reconocimiento de voz) y lee sus respuestas en voz alta (síntesis de voz). Este equipo es lo bastante potente para hacer ambas cosas por sí mismo. - local: todo se ejecuta en este equipo. Su voz se queda aquí y nada depende de internet ni de lo ocupados que estén los servidores. El instalador descarga los modelos de voz de su idioma, desde unos cientos de megabytes hasta unos pocos gigabytes, y el reconocimiento mantiene ocupado el procesador mientras habla. -- public: los servidores públicos de Open Voice OS hacen el trabajo. No hay nada que descargar, pero cada petición viaja por internet hasta servidores que comparte toda la comunidad, lo que añade retardo de red y se vuelve más lento cuando muchas personas los usan a la vez. Sus grabaciones y las respuestas se procesan en esos servidores. +- public: el trabajo lo hacen servidores de la comunidad. Se ofrecen de buena voluntad, como respaldo y como ejemplo de voz autoalojada, no como un servicio de producción: las respuestas tardan más cuando mucha gente los usa a la vez, y pueden dejar de funcionar en cualquier momento. No hay nada que descargar, pero sus grabaciones y las respuestas viajan por internet y se procesan en esos servidores. Si el reconocimiento local falla o no oye nada, esa grabación se envía a los servidores públicos. @@ -11,6 +11,6 @@ Seleccione dónde se procesa la voz: " TITLE="Instalación de Open Voice OS - Voz" LOCAL_DESCRIPTION="Procesar la voz en este equipo" -PUBLIC_DESCRIPTION="Usar los servidores públicos de Open Voice OS" +PUBLIC_DESCRIPTION="Servidores públicos (sé que pueden caerse)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/eu-es/speech.sh b/tui/locales/eu-es/speech.sh index d6155819..449d2cb3 100644 --- a/tui/locales/eu-es/speech.sh +++ b/tui/locales/eu-es/speech.sh @@ -3,7 +3,7 @@ CONTENT=" Open Voice OS-k esaten duzuna testu bihurtzen du (ahots-ezagutza) eta bere erantzunak ozen irakurtzen ditu (ahots-sintesia). Makina honek bi lanak berak egiteko adina indar du. - local: dena makina honetan exekutatzen da. Zure ahotsa hemen geratzen da, eta ezer ez dago Interneten edo zerbitzarien lan-kargaren mende. Instalatzaileak zure hizkuntzako ahots-ereduak deskargatzen ditu, ehunka megabytetik gigabyte gutxi batzuetara, eta ezagutzak prozesadorea lanpetuta mantentzen du hitz egiten duzun bitartean. - - public: Open Voice OS-en zerbitzari publikoek egiten dute lana. Ez dago ezer deskargatu beharrik, baina eskaera bakoitza Internet bidez komunitate osoak partekatzen dituen zerbitzarietara doa. Horrek sareko atzerapena gehitzen du, eta motelago doa jende askok aldi berean erabiltzen dituenean. Zure grabazioak eta erantzunak zerbitzari horietan prozesatzen dira. + - public: komunitatearen zerbitzariek egiten dute lana. Borondate onez eskaintzen dira, babes gisa eta norberak ostatatutako ahotsaren adibide gisa, ez produkzio-zerbitzu gisa: erantzunak motelago iristen dira jende askok aldi berean erabiltzen dituenean, eta edozein unetan gelditu daitezke. Ez dago ezer deskargatu beharrik, baina zure grabazioak eta erantzunak Internet bidez doaz eta zerbitzari horietan prozesatzen dira. Ezagutza lokalak huts egiten badu edo ezer entzuten ez badu, grabazio hori zerbitzari publikoetara bidaltzen da. @@ -11,6 +11,6 @@ Aukeratu non prozesatzen den ahotsa: " TITLE="Open Voice OS instalazioa - Ahotsa" LOCAL_DESCRIPTION="Prozesatu ahotsa makina honetan" -PUBLIC_DESCRIPTION="Erabili Open Voice OS-en zerbitzari publikoak" +PUBLIC_DESCRIPTION="Zerbitzari publikoak (badakit eror daitezkeela)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/fr-fr/speech.sh b/tui/locales/fr-fr/speech.sh index 2cb27472..66d2ec86 100644 --- a/tui/locales/fr-fr/speech.sh +++ b/tui/locales/fr-fr/speech.sh @@ -3,7 +3,7 @@ CONTENT=" Open Voice OS transforme ce que vous dites en texte (reconnaissance vocale) et lit ses réponses à voix haute (synthèse vocale). Cette machine est assez puissante pour faire les deux elle-même. - local : tout fonctionne sur cette machine. Votre voix reste ici, et rien ne dépend d'Internet ni de la charge des serveurs. L'installateur télécharge les modèles vocaux de votre langue, de quelques centaines de mégaoctets à quelques gigaoctets, et la reconnaissance occupe le processeur pendant que vous parlez. -- public : les serveurs publics d'Open Voice OS font le travail. Rien à télécharger, mais chaque requête passe par Internet vers des serveurs partagés par toute la communauté, ce qui ajoute du délai réseau et ralentit quand beaucoup de monde les utilise en même temps. Vos enregistrements et les réponses sont traités sur ces serveurs. +- public : des serveurs communautaires font le travail. Ils sont fournis de bonne volonté, comme solution de secours et comme exemple de traitement de la parole auto-hébergé, pas comme un service de production : les réponses ralentissent quand beaucoup de monde les utilise, et ils peuvent s'arrêter à tout moment. Rien à télécharger, mais vos enregistrements et les réponses passent par Internet et sont traités sur ces serveurs. Si la reconnaissance locale échoue ou n'entend rien, cet enregistrement-là est envoyé aux serveurs publics à la place. @@ -11,6 +11,6 @@ Veuillez sélectionner où la parole est traitée : " TITLE="Open Voice OS Installation - Traitement de la parole" LOCAL_DESCRIPTION="Traiter la parole sur cette machine" -PUBLIC_DESCRIPTION="Utiliser les serveurs publics d'Open Voice OS" +PUBLIC_DESCRIPTION="Serveurs publics (je sais qu'ils peuvent tomber)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/gl-es/speech.sh b/tui/locales/gl-es/speech.sh index 149bd72a..dcfc185b 100644 --- a/tui/locales/gl-es/speech.sh +++ b/tui/locales/gl-es/speech.sh @@ -3,7 +3,7 @@ CONTENT=" Open Voice OS converte o que dis en texto (recoñecemento de voz) e le as súas respostas en voz alta (síntese de voz). Este equipo é abondo potente para facer as dúas cousas el mesmo. - local: todo se executa neste equipo. A túa voz queda aquí e nada depende de internet nin de canto de ocupados estean os servidores. O instalador descarga os modelos de voz do teu idioma, desde uns centos de megabytes ata uns poucos gigabytes, e o recoñecemento mantén ocupado o procesador mentres falas. - - public: os servidores públicos de Open Voice OS fan o traballo. Non hai nada que descargar, pero cada petición viaxa por internet ata servidores que comparte toda a comunidade, o que engade atraso de rede e vólvese máis lento cando moita xente os usa á vez. As túas gravacións e as respostas procésanse neses servidores. + - public: o traballo fano servidores da comunidade. Ofrécense de boa vontade, como respaldo e como exemplo de voz autoaloxada, non como un servizo de produción: as respostas tardan máis cando moita xente os usa á vez, e poden deixar de funcionar en calquera momento. Non hai nada que descargar, pero as túas gravacións e as respostas viaxan por internet e procésanse neses servidores. Se o recoñecemento local falla ou non oe nada, esa gravación envíase aos servidores públicos. @@ -11,6 +11,6 @@ Selecciona onde se procesa a voz: " TITLE="Instalación de Open Voice OS - Voz" LOCAL_DESCRIPTION="Procesar a voz neste equipo" -PUBLIC_DESCRIPTION="Usar os servidores públicos de Open Voice OS" +PUBLIC_DESCRIPTION="Servidores públicos (sei que poden caer)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/hi-in/speech.sh b/tui/locales/hi-in/speech.sh index 89545ed1..746e74ec 100644 --- a/tui/locales/hi-in/speech.sh +++ b/tui/locales/hi-in/speech.sh @@ -3,7 +3,7 @@ CONTENT=" Open Voice OS आपकी कही बात को टेक्स्ट में बदलता है (वाक् पहचान) और अपने जवाब बोलकर सुनाता है (वाक् संश्लेषण)। यह मशीन ये दोनों काम ख़ुद करने के लिए काफ़ी शक्तिशाली है। - local: सब कुछ इसी मशीन पर चलता है। आपकी आवाज़ यहीं रहती है, और कुछ भी इंटरनेट या सर्वरों की व्यस्तता पर निर्भर नहीं करता। इंस्टॉलर आपकी भाषा के वाक् मॉडल डाउनलोड करता है, जो कुछ सौ मेगाबाइट से लेकर कुछ गीगाबाइट तक के होते हैं, और आपके बोलते समय पहचान प्रोसेसर को व्यस्त रखती है। - - public: Open Voice OS के सार्वजनिक सर्वर यह काम करते हैं। कुछ डाउनलोड नहीं करना पड़ता, लेकिन हर अनुरोध इंटरनेट से होकर उन सर्वरों तक जाता है जिन्हें पूरा समुदाय साझा करता है। इससे नेटवर्क की देरी जुड़ती है, और जब बहुत से लोग एक साथ इनका उपयोग करते हैं तो जवाब धीमे हो जाते हैं। आपकी रिकॉर्डिंग और जवाब इन्हीं सर्वरों पर प्रोसेस होते हैं। + - public: समुदाय के सर्वर यह काम करते हैं। इन्हें सद्भावना से चलाया जाता है, बैकअप के तौर पर और सेल्फ़-होस्टेड वाक् प्रसंस्करण के उदाहरण के रूप में, किसी प्रोडक्शन सेवा की तरह नहीं: जब बहुत से लोग एक साथ इनका उपयोग करते हैं तो जवाब धीमे हो जाते हैं, और ये सर्वर कभी भी बंद हो सकते हैं। कुछ डाउनलोड नहीं करना पड़ता, लेकिन आपकी रिकॉर्डिंग और जवाब इंटरनेट से होकर इन्हीं सर्वरों पर प्रोसेस होते हैं। अगर लोकल पहचान विफल हो जाए या कुछ न सुन पाए, तो वह रिकॉर्डिंग सार्वजनिक सर्वरों पर भेज दी जाती है। @@ -11,6 +11,6 @@ Open Voice OS आपकी कही बात को टेक्स्ट म " TITLE="Open Voice OS Installation - आवाज़" LOCAL_DESCRIPTION="इसी मशीन पर आवाज़ प्रोसेस करें" -PUBLIC_DESCRIPTION="Open Voice OS के सार्वजनिक सर्वर इस्तेमाल करें" +PUBLIC_DESCRIPTION="सार्वजनिक सर्वर (ये कभी भी बंद हो सकते हैं)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/it-it/speech.sh b/tui/locales/it-it/speech.sh index 6965f9ec..94f86b38 100644 --- a/tui/locales/it-it/speech.sh +++ b/tui/locales/it-it/speech.sh @@ -3,7 +3,7 @@ CONTENT=" Open Voice OS trasforma ciò che dici in testo (riconoscimento vocale) e legge le risposte ad alta voce (sintesi vocale). Questa macchina è abbastanza potente da fare entrambe le cose da sola. - local: tutto gira su questa macchina. La tua voce resta qui e nulla dipende da internet o da quanto sono occupati i server. L'installer scarica i modelli vocali per la tua lingua, da qualche centinaio di megabyte a qualche gigabyte, e il riconoscimento tiene occupato il processore mentre parli. - - public: il lavoro lo fanno i server pubblici di Open Voice OS. Niente da scaricare, ma ogni richiesta viaggia su internet verso server condivisi da tutta la comunità: questo aggiunge ritardo di rete e rallenta quando molte persone li usano contemporaneamente. Le tue registrazioni e le risposte vengono elaborate su quei server. + - public: il lavoro lo fanno server della comunità. Sono offerti per buona volontà, come riserva e come esempio di voce in self-hosting, non come servizio di produzione: le risposte rallentano quando molte persone li usano contemporaneamente, e possono andare offline in qualsiasi momento. Niente da scaricare, ma le tue registrazioni e le risposte viaggiano su internet e vengono elaborate su quei server. Se il riconoscimento locale fallisce o non sente nulla, quella registrazione viene inviata ai server pubblici. @@ -11,6 +11,6 @@ Seleziona dove elaborare la voce: " TITLE="Installazione di Open Voice OS - Voce" LOCAL_DESCRIPTION="Elaborare la voce su questa macchina" -PUBLIC_DESCRIPTION="Usare i server pubblici di Open Voice OS" +PUBLIC_DESCRIPTION="Server pubblici (so che possono andare offline)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/kab-dz/speech.sh b/tui/locales/kab-dz/speech.sh index 9ae47111..081feae1 100644 --- a/tui/locales/kab-dz/speech.sh +++ b/tui/locales/kab-dz/speech.sh @@ -3,7 +3,7 @@ CONTENT=" Open Voice OS yettbeddil ayen i d-tenniḍ ɣer uḍris (aɛqal n taɣect) yerna yeqqar-d tiririyin-is s taɣect (asuddes n taɣect). Tamacint-a tezmer ad txeddem snat-a s yiman-is. - local: kullec ad iteddu ɣef tmacint-a. Taɣect-ik ad teqqim da, ulac ayen icudden ɣer Internet neɣ ɣer yiqeddacen. Amesbeddi ad d-yessader timudmin n taɣect n tutlayt-ik, seg kra n yimiḍ n MB alamma d kra n GB, yerna aɛqal ad yesseqdec amesbadu mi ara tettmeslayeḍ. - - public: d iqeddacen izayazen n Open Voice OS ara ixedmen axeddim. Ulac ayen ara d-tessadreḍ, maca yal asuter ad yeddu s Internet ɣer yiqeddacen i tebḍa merra temɣiwant: aya yettarra-d aɛettel, yerna ad ẓẓayen mi ara ten-sqedcen aṭas n medden ɣef tikkelt. Iseklasen-ik d tririyin ad ttwasenfaren ɣef yiqeddacen-a. + - public: d iqeddacen n temɣiwant ara ixedmen axeddim. Ttwafken-d s lebɣi, am tarrayt n uɛiwen d umedya n taɣect i yesbedden yiman-is, maci am umeẓlu n tfalit: tiririyin ad ẓẓayent mi ara ten-sqedcen aṭas n medden ɣef tikkelt, yerna zemren ad ḥbesen melmi tebɣu tili. Ulac ayen ara d-tessadreḍ, maca iseklasen-ik d tririyin ad ɛeddin s Internet u ad ttwasenfaren ɣef yiqeddacen-a. Ma yecceḍ uɛqal adigan neɣ ur yesla i kra, asekles-nni ad yettwazen ɣer yiqeddacen izayazen. @@ -11,6 +11,6 @@ Ttxil-k fren anida ara tettwasenfar taɣect: " TITLE="Asbeddi n Open Voice OS - Taɣect" LOCAL_DESCRIPTION="Taɣect ɣef tmacint-a" -PUBLIC_DESCRIPTION="Iqeddacen izayazen n Open Voice OS" +PUBLIC_DESCRIPTION="Iqeddacen izayazen (ẓriɣ zemren ad ḥbesen)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/nl-nl/speech.sh b/tui/locales/nl-nl/speech.sh index d21fe7c2..fa6d73fd 100644 --- a/tui/locales/nl-nl/speech.sh +++ b/tui/locales/nl-nl/speech.sh @@ -3,7 +3,7 @@ CONTENT=" Open Voice OS zet wat je zegt om in tekst (spraakherkenning) en leest zijn antwoorden voor (spraaksynthese). Deze machine is krachtig genoeg om beide zelf te doen. - local: alles draait op deze machine. Je stem blijft hier, en niets hangt af van internet of van hoe druk de servers zijn. Het installatieprogramma downloadt de spraakmodellen voor je taal, van een paar honderd megabyte tot een paar gigabyte, en de herkenning houdt de processor bezig terwijl je praat. -- public: de openbare servers van Open Voice OS doen het werk. Je hoeft niets te downloaden, maar elk verzoek gaat via internet naar servers die de hele community deelt. Dat voegt netwerkvertraging toe en wordt trager wanneer veel mensen ze tegelijk gebruiken. Je opnames en de antwoorden worden op die servers verwerkt. +- public: servers van de community doen het werk. Ze draaien uit goede wil, als reserve en als voorbeeld van zelf gehoste spraakverwerking, niet als productiedienst: antwoorden duren langer wanneer veel mensen ze tegelijk gebruiken, en de servers kunnen op elk moment uitvallen. Je hoeft niets te downloaden, maar je opnames en de antwoorden gaan via internet en worden op die servers verwerkt. Als de lokale herkenning mislukt of niets hoort, wordt die ene opname naar de openbare servers gestuurd. @@ -11,6 +11,6 @@ Selecteer waar spraak wordt verwerkt: " TITLE="Open Voice OS Installatie - Spraak" LOCAL_DESCRIPTION="Spraak op deze machine verwerken" -PUBLIC_DESCRIPTION="De openbare Open Voice OS-servers gebruiken" +PUBLIC_DESCRIPTION="Openbare servers (ik weet dat ze kunnen uitvallen)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/pl-pl/speech.sh b/tui/locales/pl-pl/speech.sh index 6761afd0..63217eca 100644 --- a/tui/locales/pl-pl/speech.sh +++ b/tui/locales/pl-pl/speech.sh @@ -3,7 +3,7 @@ CONTENT=" Open Voice OS zamienia to, co mówisz, na tekst (rozpoznawanie mowy) i czyta swoje odpowiedzi na głos (synteza mowy). Ten komputer jest na tyle wydajny, żeby robić jedno i drugie samodzielnie. - local: wszystko działa na tym komputerze. Twój głos zostaje tutaj i nic nie zależy od internetu ani od obciążenia serwerów. Instalator pobiera modele mowy dla Twojego języka, od kilkuset megabajtów do kilku gigabajtów, a rozpoznawanie obciąża procesor, gdy mówisz. -- public: pracę wykonują publiczne serwery Open Voice OS. Nic nie trzeba pobierać, ale każde żądanie wędruje przez internet do serwerów współdzielonych przez całą społeczność, co dodaje opóźnienie sieciowe i spowalnia działanie, gdy korzysta z nich wiele osób naraz. Twoje nagrania i odpowiedzi są przetwarzane na tych serwerach. +- public: pracę wykonują serwery społeczności. Działają w dobrej wierze, jako zapas i przykład samodzielnie hostowanego przetwarzania mowy, a nie jako usługa produkcyjna: odpowiedzi zwalniają, gdy korzysta z nich wiele osób naraz, a serwery w każdej chwili mogą przestać działać. Nic nie trzeba pobierać, ale Twoje nagrania i odpowiedzi trafiają przez internet na te serwery i tam są przetwarzane. Jeśli lokalne rozpoznawanie zawiedzie lub nic nie usłyszy, to nagranie zostanie wysłane do serwerów publicznych. @@ -11,6 +11,6 @@ Wybierz, gdzie ma być przetwarzana mowa: " TITLE="Instalacja Open Voice OS - Mowa" LOCAL_DESCRIPTION="Przetwarzaj mowę na tym komputerze" -PUBLIC_DESCRIPTION="Używaj publicznych serwerów Open Voice OS" +PUBLIC_DESCRIPTION="Serwery publiczne (wiem, że mogą przestać działać)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/pt-pt/speech.sh b/tui/locales/pt-pt/speech.sh index b5cf7fd6..f4fe6c30 100644 --- a/tui/locales/pt-pt/speech.sh +++ b/tui/locales/pt-pt/speech.sh @@ -3,7 +3,7 @@ CONTENT=" O Open Voice OS converte o que diz em texto (reconhecimento de voz) e lê as respostas em voz alta (síntese de voz). Esta máquina é suficientemente potente para fazer as duas coisas sozinha. - local: tudo corre nesta máquina. A sua voz fica aqui e nada depende da internet nem da ocupação dos servidores. O instalador descarrega os modelos de voz do seu idioma, de algumas centenas de megabytes a alguns gigabytes, e o reconhecimento mantém o processador ocupado enquanto fala. - - public: os servidores públicos do Open Voice OS fazem o trabalho. Não há nada para descarregar, mas cada pedido viaja pela internet até servidores partilhados por toda a comunidade, o que acrescenta atraso de rede e fica mais lento quando muitas pessoas os usam ao mesmo tempo. As suas gravações e as respostas são processadas nesses servidores. + - public: o trabalho é feito por servidores da comunidade. São disponibilizados por boa vontade, como reserva e como exemplo de voz auto-alojada, não como um serviço de produção: as respostas demoram mais quando muitas pessoas os usam ao mesmo tempo, e podem ficar indisponíveis a qualquer momento. Não há nada para descarregar, mas as suas gravações e as respostas viajam pela internet e são processadas nesses servidores. Se o reconhecimento local falhar ou não ouvir nada, essa gravação é enviada para os servidores públicos. @@ -11,6 +11,6 @@ Seleccione onde a voz é processada: " TITLE="Open Voice OS Instalação - Voz" LOCAL_DESCRIPTION="Processar a voz nesta máquina" -PUBLIC_DESCRIPTION="Usar os servidores públicos do Open Voice OS" +PUBLIC_DESCRIPTION="Servidores públicos (sei que podem falhar)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION From cb483113c951985c3665a1b1778f4551e29a9cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Tue, 6 Oct 2026 18:04:16 -0400 Subject: [PATCH 04/14] Prove local speech works before configuring it Running every installer locale through its configured voice and recognizer on the alpha stack showed the ovos-config recommendations cannot be taken on trust there: phoonnx 1.3.4a1 does not know the Catalan voice and cannot speak any Spanish voice, the Portuguese recognizer needs 5 GB of memory, and Polish, Hindi and Kabyle have no recommendation at all. Configured blindly, a device would come up mute or deaf. One script now replaces the two. For each half it tries ovos-config's recommendation, then the plugin's own choice for the language, and keeps the first that works on the machine: the recognizer must load and run within a 3 GB budget, the voice must actually speak. Trying is also what downloads the model, each in its own process so a rejected one gives its memory back, and a rejected recognizer is deleted. A model whose repository ships int8 weights the recommendation does not ask for gets them. Only what passed is written; what failed is reported and stays on the public servers. Co-Authored-By: Claude Opus 5.5 --- ansible/roles/ovos_config/defaults/main.yml | 5 +- .../ovos_config/files/speech_prefetch.py | 28 -- .../files/speech_recommendation.py | 49 ---- .../roles/ovos_config/files/speech_setup.py | 189 +++++++++++++ ansible/roles/ovos_config/tasks/speech.yml | 77 ++---- scripts/test_speech_setup.py | 251 ++++++++++++++++++ tests/bats/code_quality.bats | 10 +- 7 files changed, 474 insertions(+), 135 deletions(-) delete mode 100644 ansible/roles/ovos_config/files/speech_prefetch.py delete mode 100644 ansible/roles/ovos_config/files/speech_recommendation.py create mode 100644 ansible/roles/ovos_config/files/speech_setup.py create mode 100644 scripts/test_speech_setup.py diff --git a/ansible/roles/ovos_config/defaults/main.yml b/ansible/roles/ovos_config/defaults/main.yml index a8f3e06d..aa874316 100644 --- a/ansible/roles/ovos_config/defaults/main.yml +++ b/ansible/roles/ovos_config/defaults/main.yml @@ -87,8 +87,9 @@ ovos_config_homescreen_probe: >- # The public STT server stays configured as the fallback for an utterance the # local plugin fails on or hears nothing in. ovos_config_speech_stt_fallback: ovos-stt-plugin-server -ovos_config_speech_prefetch_retries: "{{ ovos_installer_speech_prefetch_retries | default(3) }}" -ovos_config_speech_prefetch_delay: "{{ ovos_installer_speech_prefetch_delay | default(10) }}" +# A recognizer that needs more than this is skipped for the next candidate: the +# machines local speech targets have 8 GB, which the rest of OVOS shares. +ovos_config_speech_stt_memory_budget_mb: "{{ ovos_installer_speech_stt_memory_budget_mb | default(3072) }}" # The stt and tts sections the last run wrote, carried through the template so a # re-run does not drop them only for speech.yml to put them back. ovos_config_speech_current: {} diff --git a/ansible/roles/ovos_config/files/speech_prefetch.py b/ansible/roles/ovos_config/files/speech_prefetch.py deleted file mode 100644 index e3f4a6ff..00000000 --- a/ansible/roles/ovos_config/files/speech_prefetch.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Load the configured local STT and TTS plugins once. - -Usage: speech_prefetch.py - -Both plugins fetch their model when they are created, so doing it here moves -a download of up to a few gigabytes from the first utterance to the install. -""" -import base64 -import json -import sys - -from ovos_plugin_manager.stt import load_stt_plugin -from ovos_plugin_manager.tts import load_tts_plugin - -sections, lang = json.loads(base64.b64decode(sys.argv[1])), sys.argv[2] - -for kind, load in (("stt", load_stt_plugin), ("tts", load_tts_plugin)): - section = sections.get(kind) - if not section: - continue - module = section["module"] - plugin = load(module) - if plugin is None: - sys.exit(f"{kind} plugin {module} is not installed") - config = dict(section.get(module) or {}) - config.setdefault("lang", lang) - plugin(config=config) - print(f"{kind}: {module} ready") diff --git a/ansible/roles/ovos_config/files/speech_recommendation.py b/ansible/roles/ovos_config/files/speech_recommendation.py deleted file mode 100644 index 7be0bae1..00000000 --- a/ansible/roles/ovos_config/files/speech_recommendation.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Print the stt and tts sections ovos-config recommends for on-device speech. - -Usage: speech_recommendation.py - -Reads the offline recommendations shipped with the ovos-config installed in -the OVOS virtualenv - the files `ovos-config autoconfigure --offline` merges - -so the plugins and models follow that release rather than a copy kept here. -Prints {"stt": {...} or null, "tts": {...} or null}; null means the release -has no recommendation for the locale. -""" -import json -import os -import sys - -import ovos_config -from ovos_config.config import LocalConf - -lang, gender = sys.argv[1], sys.argv[2] -root = os.path.join(os.path.dirname(ovos_config.__file__), "recommends") - - -def find(folder): - path = os.path.join(root, folder) - if not os.path.isdir(path): - return None - try: - from ovos_config.utils import find_recommends_file - except ImportError: - # ovos-config 2.x: the exact tag, then a file sharing its primary subtag. - exact = os.path.join(path, f"{lang.lower()}.conf") - if os.path.isfile(exact): - return exact - primary = lang.lower().split("-")[0] - for name in sorted(os.listdir(path)): - if name.endswith(".conf") and name.lower().startswith(primary): - return os.path.join(path, name) - return None - return find_recommends_file(path, lang) - - -def section(folder, key): - path = find(folder) - return LocalConf(path).get(key) if path else None - - -print(json.dumps({ - "stt": section("offline_stt", "stt"), - "tts": section(f"offline_{gender}", "tts"), -})) diff --git a/ansible/roles/ovos_config/files/speech_setup.py b/ansible/roles/ovos_config/files/speech_setup.py new file mode 100644 index 00000000..a2a7c3eb --- /dev/null +++ b/ansible/roles/ovos_config/files/speech_setup.py @@ -0,0 +1,189 @@ +"""Pick, fetch and prove the on-device speech plugins for a locale. + +Usage: speech_setup.py [STT memory budget in MB] + +For each half asked for, it tries the configuration the installed ovos-config +recommends for the locale - the files `ovos-config autoconfigure --offline` +merges - and then the plugin's own choice for the language, and keeps the first +one that works on this machine: the recognizer has to load and run within the +memory budget, the voice has to actually speak. A recommendation alone is not +enough: one names a voice the installed phoonnx does not know, and another a +model that needs 5 GB of memory. Loading is also what downloads the model, so a +kept choice is ready for the first utterance. + +Prints {"stt": section or null, "tts": section or null, "notes": [...]}. null +means nothing worked here, and that half stays on the public servers. +""" +import json +import os +import re +import shutil +import subprocess +import sys + +import ovos_config +from ovos_config.config import LocalConf + +kinds, lang, gender = sys.argv[1].split(","), sys.argv[2], sys.argv[3] +budget_mb = int(sys.argv[4]) if len(sys.argv) > 4 else 3072 +recommends = os.path.join(os.path.dirname(ovos_config.__file__), "recommends") +notes = [] + +# Each candidate is tried in a process of its own: a rejected 5 GB model then +# gives its memory back before the next one loads, instead of stacking up on an +# 8 GB board, and the peak this reports is that candidate's alone. +TRY = r''' +import json, os, resource, sys, tempfile +kind, lang, section = sys.argv[1], sys.argv[2], json.loads(sys.argv[3]) +module = section["module"] +config = dict(section.get(module) or {}, lang=lang) +if kind == "stt": + from ovos_plugin_manager.stt import load_stt_plugin + from ovos_plugin_manager.utils.audio import AudioData + plugin = load_stt_plugin(module) + if plugin is None: + sys.exit(f"{module} is not installed") + plugin(config=config).execute(AudioData(b"\0\0" * 16000, 16000, 2), language=lang) +else: + from ovos_plugin_manager.tts import load_tts_plugin + plugin = load_tts_plugin(module) + if plugin is None: + sys.exit(f"{module} is not installed") + with tempfile.TemporaryDirectory() as tmp: + wav = os.path.join(tmp, "probe.wav") + plugin(config=config).get_tts("Open Voice OS. 1, 2, 3.", wav) + if os.path.getsize(wav) <= 44: + sys.exit("the voice produced no audio") +peak = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss +print(json.dumps({"peak_mb": peak // (1048576 if sys.platform == "darwin" else 1024)})) +''' + + +def find(folder): + path = os.path.join(recommends, folder) + if not os.path.isdir(path): + return None + try: + from ovos_config.utils import find_recommends_file + except ImportError: + # ovos-config 2.x: the exact tag, then a file sharing its primary subtag. + exact = os.path.join(path, f"{lang.lower()}.conf") + if os.path.isfile(exact): + return exact + primary = lang.lower().split("-")[0] + for name in sorted(os.listdir(path)): + if name.endswith(".conf") and name.lower().startswith(primary): + return os.path.join(path, name) + return None + return find_recommends_file(path, lang) + + +def recommended(folder, key): + path = find(folder) + return LocalConf(path).get(key) if path else None + + +def prefer_int8(stt): + """Ask for int8 weights where the model's repository ships them. + + A recommendation names a model, not its size: Spanish names a 1.1B model and + no quantization, which loads 4.4 GB of fp32 weights next to an int8 copy of + 1.1 GB. Without the hub (offline, older images) the section is kept as is. + """ + module = (stt or {}).get("module") + options = dict((stt or {}).get(module) or {}) + model = options.get("model") or "" + if "/" not in model or options.get("quantization"): + return stt + try: + from huggingface_hub import HfApi + files = HfApi().list_repo_files(model) + except Exception: + return stt + if any(re.search(r"[._]int8\.onnx$", name) for name in files): + options["quantization"] = "int8" + stt = dict(stt, **{module: options}) + return stt + + +def stt_candidates(): + rec = recommended("offline_stt", "stt") + if rec: + yield "ovos-config's recommendation", prefer_int8(rec) + try: + from ovos_stt_plugin_onnxasr.defaults import resolve_model + model = resolve_model(lang, {}) + except Exception: + model = None + if model: + module = "ovos-stt-plugin-onnx-asr" + yield "the plugin's own model for the language", prefer_int8( + {"module": module, "fallback_module": "", module: {"model": model}}) + + +def tts_candidates(): + rec = recommended(f"offline_{gender}", "tts") + if rec: + yield "ovos-config's recommendation", rec + try: + from phoonnx.model_manager import TTSModelManager + manager = TTSModelManager() + manager.load() + manager.merge_default_voices() + voices = [voice.voice_id for voice in manager.get_lang_voices(lang)] + except Exception: + voices = [] + module = "ovos-tts-plugin-phoonnx" + # phoonnx lists a language's voices best first; a few tries are enough to + # get past one that a phoonnx release cannot run. + for voice in voices[:4]: + yield "phoonnx's own voice for the language", {"module": module, module: {"voice": voice}} + + +def model_of(section): + options = section.get(section["module"]) or {} + return options.get("model") or options.get("voice") or section["module"] + + +def forget_model(model): + """Delete a rejected recognizer model, so it does not hold the disk until uninstall.""" + try: + from ovos_utils.xdg_utils import xdg_data_home + except ImportError: + return + shutil.rmtree(os.path.join(xdg_data_home(), "ovos_stt_plugin_onnxasr", model.replace("/", "--")), + ignore_errors=True) + + +def first_working(kind, candidates): + tried = set() + for origin, section in candidates: + model = model_of(section) + if model in tried: + continue + tried.add(model) + run = subprocess.run([sys.executable, "-c", TRY, kind, lang, json.dumps(section)], + capture_output=True, text=True) + lines = [line for line in run.stdout.splitlines() if line.startswith("{")] + if run.returncode != 0 or not lines: + reason = (run.stderr.strip().splitlines() or ["it did not start"])[-1] + notes.append(f"{kind.upper()} {model} ({origin}) does not work here: {reason[:200]}") + continue + peak = json.loads(lines[-1])["peak_mb"] + if kind == "stt" and peak > budget_mb: + notes.append(f"STT {model} ({origin}) needs {peak} MB, above the {budget_mb} MB budget") + forget_model(model) + continue + if origin != "ovos-config's recommendation": + notes.append(f"{kind.upper()} uses {model}, {origin}") + return section + return None + + +result = {"stt": None, "tts": None} +if "stt" in kinds: + result["stt"] = first_working("stt", stt_candidates()) +if "tts" in kinds: + result["tts"] = first_working("tts", tts_candidates()) +result["notes"] = notes +print(json.dumps(result)) diff --git a/ansible/roles/ovos_config/tasks/speech.yml b/ansible/roles/ovos_config/tasks/speech.yml index 8921d7c4..28f57157 100644 --- a/ansible/roles/ovos_config/tasks/speech.yml +++ b/ansible/roles/ovos_config/tasks/speech.yml @@ -1,45 +1,53 @@ --- -# Local speech: STT and TTS run on this host with the plugins and models the -# installed ovos-config recommends for the locale. Those are the files -# `ovos-config autoconfigure --offline` reads, but only their stt and tts -# sections are taken: autoconfigure also merges per-language defaults for units, -# wake words and intents, which this installer sets itself. +# Local speech: STT and TTS run on this host. speech_setup.py picks the plugins and +# models - ovos-config's recommendation for the locale, or the plugin's own choice +# for the language when that does not work here - downloads them, and proves they +# run before anything is written: a voice the installed phoonnx cannot speak, or a +# model too big for the machine, would otherwise leave a device mute or deaf. Only +# the stt and tts sections are taken: `ovos-config autoconfigure` also merges +# per-language defaults for units, wake words and intents, which this installer +# sets itself. # # The caller passes ovos_config_speech_python, the OVOS virtualenv's Python. -- name: Resolve the speech plugins ovos-config recommends for this locale +- name: Pick, download and check the local speech plugins become: true become_user: "{{ ovos_installer_user }}" ansible.builtin.command: argv: - "{{ ovos_config_speech_python }}" - -c - - "{{ lookup('ansible.builtin.file', 'speech_recommendation.py') }}" + - "{{ lookup('ansible.builtin.file', 'speech_setup.py') }}" + - stt,tts - "{{ ovos_installer_locale }}" - "{{ 'female' if ovos_installer_ovos_config_tts_gender == '--female' else 'male' }}" - register: ovos_config_speech_probe + - "{{ ovos_config_speech_stt_memory_budget_mb }}" + register: ovos_config_speech_setup changed_when: false # A failed local recognition, or one that hears nothing, goes to the public # server for that utterance instead of leaving the user unanswered. -- name: Keep the recommended sections +- name: Keep what works on this machine vars: - _recommended: "{{ ovos_config_speech_probe.stdout | from_json }}" + _setup: "{{ ovos_config_speech_setup.stdout | from_json }}" ansible.builtin.set_fact: ovos_config_speech_wanted: >- {{ - ({'stt': _recommended.stt | combine({'fallback_module': ovos_config_speech_stt_fallback})} - if _recommended.stt else {}) - | combine({'tts': _recommended.tts} if _recommended.tts else {}) + ({'stt': _setup.stt | combine({'fallback_module': ovos_config_speech_stt_fallback})} + if _setup.stt else {}) + | combine({'tts': _setup.tts} if _setup.tts else {}) }} + ovos_config_speech_notes: "{{ _setup.notes | default([]) }}" + +- name: Report what the local speech setup had to work around + ansible.builtin.debug: + msg: "{{ item }}" + loop: "{{ ovos_config_speech_notes }}" -# ovos-config does not recommend on-device speech for every language, and which -# ones it covers changes from one release to the next. - name: Report what stays on the public servers ansible.builtin.debug: msg: >- - The ovos-config release in this virtualenv recommends no on-device - {{ item | upper }} for {{ ovos_installer_locale }}, so {{ item | upper }} - stays on the public servers. + No on-device {{ item | upper }} works for {{ ovos_installer_locale }} on this + machine, so {{ item | upper }} stays on the public servers. loop: "{{ ['stt', 'tts'] | difference(ovos_config_speech_wanted.keys() | list) }}" - name: Read mycroft.conf for the speech configuration @@ -93,36 +101,3 @@ ansible.builtin.set_fact: ovos_config_speech_stt_engine: "{{ 'local' if _applied and 'stt' in ovos_config_speech_wanted else 'public' }}" ovos_config_speech_tts_engine: "{{ 'local' if _applied and 'tts' in ovos_config_speech_wanted else 'public' }}" - -# Both plugins fetch their model when they are created. Doing that here turns a -# download of up to a few gigabytes into part of the install, instead of a -# listener that sits silent through its first start. -- name: Download the local speech models - when: - - ovos_config_speech_conf_readable | default(false) | bool - - ovos_config_speech_wanted | length > 0 - block: - - name: Load the local speech plugins once so they fetch their models - become: true - become_user: "{{ ovos_installer_user }}" - ansible.builtin.command: - argv: - - "{{ ovos_config_speech_python }}" - - -c - - "{{ lookup('ansible.builtin.file', 'speech_prefetch.py') }}" - # Encoded because Ansible turns a templated string that looks like a - # dict back into one, and the script would get its Python repr. - - "{{ ovos_config_speech_wanted | to_json | b64encode }}" - - "{{ ovos_installer_locale }}" - register: ovos_config_speech_prefetch - changed_when: false - until: ovos_config_speech_prefetch is success - retries: "{{ ovos_config_speech_prefetch_retries | int }}" - delay: "{{ ovos_config_speech_prefetch_delay | int }}" - rescue: - - name: Report the models that could not be downloaded - ansible.builtin.debug: - msg: >- - The local speech models could not be downloaded - ({{ (ovos_config_speech_prefetch.stderr_lines | default(['unknown error'], true)) | last }}). - The speech services download them when they start instead. diff --git a/scripts/test_speech_setup.py b/scripts/test_speech_setup.py new file mode 100644 index 00000000..4dc6161f --- /dev/null +++ b/scripts/test_speech_setup.py @@ -0,0 +1,251 @@ +"""What the local speech setup keeps, and what it refuses. + +ovos_config/files/speech_setup.py runs inside the OVOS virtualenv, or the listener and +audio containers, and decides what the device will use for speech. It is driven here +against stand-ins for ovos-config (both the 2.x and the 3.x way of finding a language), +the Hugging Face hub, the plugin manager and the two plugins' own per-language choices, +so each fallback can be made to happen on purpose. The candidates are still tried in real +child processes, which is where the memory budget is measured. +""" +import json +import os +import subprocess +import sys +import tempfile +import textwrap +import unittest +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +SETUP = ROOT / "ansible" / "roles" / "ovos_config" / "files" / "speech_setup.py" +STT = "ovos-stt-plugin-onnx-asr" +TTS = "ovos-tts-plugin-phoonnx" + +STUBS = { + "ovos_config/config.py": """ + import json + + + class LocalConf(dict): + def __init__(self, path): + super().__init__(json.load(open(path))) + """, + "ovos_plugin_manager/__init__.py": "", + "ovos_plugin_manager/utils/__init__.py": "", + "ovos_plugin_manager/utils/audio.py": """ + class AudioData: + def __init__(self, frames, rate, width): + self.frames = frames + """, + # A model whose name starts with "broken" fails to load, one starting with "huge" + # holds 200 MB once loaded; anything else loads and hears nothing. + "ovos_plugin_manager/stt.py": """ + class Recognizer: + def __init__(self, config): + model = config.get("model", "") + if model.startswith("broken"): + raise RuntimeError("cannot load " + model) + self.weights = b"x" * (200 * 1024 * 1024) if model.startswith("huge") else b"" + + def execute(self, audio, language=None): + return "" + + + def load_stt_plugin(module): + return Recognizer if module == "ovos-stt-plugin-onnx-asr" else None + """, + # A voice whose name starts with "mute" loads but cannot speak, like the Spanish + # voices in phoonnx 1.3.4a1. + "ovos_plugin_manager/tts.py": """ + import wave + + + class Voice: + def __init__(self, config): + self.voice = config.get("voice", "") + + def get_tts(self, text, path): + if self.voice.startswith("mute"): + raise wave.Error("# channels not specified") + with wave.open(path, "wb") as out: + out.setnchannels(1) + out.setsampwidth(2) + out.setframerate(16000) + out.writeframes(b"\\0\\0" * 1600) + + + def load_tts_plugin(module): + return Voice if module == "ovos-tts-plugin-phoonnx" else None + """, + "ovos_stt_plugin_onnxasr/__init__.py": "", + "ovos_stt_plugin_onnxasr/defaults.py": """ + import json, os + + REGISTRY = json.loads(os.environ.get("STUB_STT_REGISTRY", "{}")) + + + def resolve_model(lang, lang2model): + return REGISTRY.get(lang.lower().split("-")[0]) + """, + "phoonnx/__init__.py": "", + "phoonnx/model_manager.py": """ + import json, os + + VOICES = json.loads(os.environ.get("STUB_VOICES", "{}")) + + + class Voice: + def __init__(self, voice_id): + self.voice_id = voice_id + + + class TTSModelManager: + def load(self): + pass + + def merge_default_voices(self): + pass + + def get_lang_voices(self, lang): + return [Voice(v) for v in VOICES.get(lang.lower().split("-")[0], [])] + """, + "ovos_utils/__init__.py": "", + "ovos_utils/xdg_utils.py": """ + import os + + + def xdg_data_home(): + return os.environ["STUB_XDG_DATA_HOME"] + """, + "huggingface_hub/__init__.py": """ + import json, os + + + class HfApi: + def list_repo_files(self, repo): + return json.loads(os.environ.get("STUB_HUB", "{}")).get(repo, []) + """, +} + + +def stt(model, **options): + return {"stt": {"module": STT, "fallback_module": "", STT: dict(model=model, **options)}} + + +def tts(voice): + return {"tts": {"module": TTS, TTS: {"voice": voice}}} + + +class SpeechSetupTest(unittest.TestCase): + + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = Path(self.tmp.name) + self.data = self.root / "data" + self.data.mkdir() + for path, content in STUBS.items(): + target = self.root / "stubs" / path + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(textwrap.dedent(content)) + self.env = dict(os.environ, PYTHONPATH=str(self.root / "stubs"), STUB_XDG_DATA_HOME=str(self.data)) + + def tearDown(self): + self.tmp.cleanup() + + def recommends(self, files, find_by_distance=False): + package = self.root / "stubs" / "ovos_config" + (package / "__init__.py").write_text("") + if find_by_distance: + (package / "utils.py").write_text(textwrap.dedent(""" + import os + + + def find_recommends_file(folder, lang): + primary = lang.lower().split("-")[0] + for name in sorted(os.listdir(folder)): + if name.lower().startswith(primary): + return os.path.join(folder, name) + return None + """)) + for path, content in files.items(): + target = package / "recommends" / path + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(json.dumps(content)) + + def setup(self, lang, kinds="stt,tts", gender="male", budget=100, registry=None, voices=None, hub=None): + env = dict(self.env, STUB_STT_REGISTRY=json.dumps(registry or {}), + STUB_VOICES=json.dumps(voices or {}), STUB_HUB=json.dumps(hub or {})) + out = subprocess.run([sys.executable, str(SETUP), kinds, lang, gender, str(budget)], + capture_output=True, text=True, env=env, check=True).stdout + return json.loads(out) + + def test_working_recommendations_are_kept_as_they_are(self): + self.recommends({"offline_stt/en-us.conf": stt("parakeet", quantization="int8"), + "offline_male/en-us.conf": tts("OpenVoiceOS/pipertts_en-US_miro")}) + result = self.setup("en-us") + self.assertEqual(result["stt"][STT], {"model": "parakeet", "quantization": "int8"}) + self.assertEqual(result["tts"][TTS]["voice"], "OpenVoiceOS/pipertts_en-US_miro") + self.assertEqual(result["notes"], []) + + def test_2x_finds_a_language_by_its_primary_subtag(self): + self.recommends({"offline_male/da-dk.conf": tts("OpenVoiceOS/phoonnx_da-DK_miro_espeak")}) + self.assertEqual(self.setup("da", kinds="tts")["tts"][TTS]["voice"], "OpenVoiceOS/phoonnx_da-DK_miro_espeak") + + def test_3x_asks_ovos_config_for_the_closest_tag(self): + self.recommends({"offline_stt/kab.conf": stt("OpenVoiceOS/nvidia-kab-conformer")}, find_by_distance=True) + self.assertEqual(self.setup("kab-dz", kinds="stt")["stt"][STT]["model"], "OpenVoiceOS/nvidia-kab-conformer") + + def test_a_recognizer_that_does_not_load_gives_way_to_the_plugins_own_model(self): + self.recommends({"offline_stt/ca-es.conf": stt("broken/ca-model")}) + result = self.setup("ca-es", kinds="stt", registry={"ca": "OpenVoiceOS/nvidia-ca-conformer"}) + self.assertEqual(result["stt"][STT]["model"], "OpenVoiceOS/nvidia-ca-conformer") + self.assertTrue(any("broken/ca-model" in note for note in result["notes"])) + + def test_a_recognizer_over_the_memory_budget_gives_way_and_is_deleted(self): + self.recommends({"offline_stt/pt-pt.conf": stt("huge/whisper-medium-pt")}) + downloaded = self.data / "ovos_stt_plugin_onnxasr" / "huge--whisper-medium-pt" + downloaded.mkdir(parents=True) + result = self.setup("pt-pt", kinds="stt", budget=100, registry={"pt": "OpenVoiceOS/parakeet-pt"}) + self.assertEqual(result["stt"][STT]["model"], "OpenVoiceOS/parakeet-pt") + self.assertTrue(any("above the 100 MB budget" in note for note in result["notes"])) + self.assertFalse(downloaded.exists()) + + def test_a_voice_that_cannot_speak_gives_way_to_the_next_phoonnx_voice(self): + self.recommends({"offline_male/es-es.conf": tts("mute/pipertts_es-ES_miro")}) + result = self.setup("es-es", kinds="tts", voices={"es": ["mute/pipertts_es-ES_miro", "piper/es_ES-davefx-medium"]}) + self.assertEqual(result["tts"][TTS]["voice"], "piper/es_ES-davefx-medium") + + def test_a_language_without_a_recommendation_uses_the_plugins_own_choice(self): + self.recommends({}) + result = self.setup("pl-pl", registry={"pl": "OpenVoiceOS/parakeet-pl"}, voices={"pl": ["piper/pl_PL-gosia-medium"]}) + self.assertEqual(result["stt"][STT]["model"], "OpenVoiceOS/parakeet-pl") + self.assertEqual(result["stt"]["fallback_module"], "") + self.assertEqual(result["tts"][TTS]["voice"], "piper/pl_PL-gosia-medium") + + def test_when_nothing_works_both_halves_stay_public(self): + self.recommends({"offline_stt/es-es.conf": stt("broken/es"), "offline_male/es-es.conf": tts("mute/es")}) + result = self.setup("es-es", voices={"es": ["mute/es", "mute/other"]}) + self.assertIsNone(result["stt"]) + self.assertIsNone(result["tts"]) + + def test_int8_is_asked_for_where_the_repository_ships_it(self): + model = "OpenVoiceOS/parakeet-rnnt-1.1b-es" + self.recommends({"offline_stt/es-es.conf": stt(model)}) + result = self.setup("es-es", kinds="stt", hub={model: ["encoder-model.onnx_data", "encoder-model.int8.onnx"]}) + self.assertEqual(result["stt"][STT]["quantization"], "int8") + + def test_a_repository_without_int8_weights_is_left_alone(self): + model = "OpenVoiceOS/whisper-medium-pt" + self.recommends({"offline_stt/pt-pt.conf": stt(model)}) + result = self.setup("pt-pt", kinds="stt", hub={model: ["encoder_model.onnx_data"]}) + self.assertNotIn("quantization", result["stt"][STT]) + + def test_only_the_halves_asked_for_are_set_up(self): + self.recommends({"offline_stt/en-us.conf": stt("parakeet"), "offline_male/en-us.conf": tts("miro")}) + result = self.setup("en-us", kinds="stt") + self.assertIsNotNone(result["stt"]) + self.assertIsNone(result["tts"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/bats/code_quality.bats b/tests/bats/code_quality.bats index 1372b9fb..f2442ea9 100644 --- a/tests/bats/code_quality.bats +++ b/tests/bats/code_quality.bats @@ -5136,14 +5136,14 @@ for template in ('core-requirements.txt.j2', 'satellite-requirements.txt.j2'): run bash -c "awk '/Configure local speech/,0' '$venv_tasks' | grep -q \"ovos_installer_speech_engine | default('public')) == 'local'\"" assert_success - # Only the stt and tts sections are written, the public STT server stays as the - # fallback, and the models are fetched during the install. + # Only what speech_setup.py proved works here is written - it also downloads the + # models, within a memory budget - and the public STT server stays as the fallback. local speech_tasks="ansible/roles/ovos_config/tasks/speech.yml" - run grep -q "speech_recommendation.py" "$speech_tasks" + run grep -q "speech_setup.py" "$speech_tasks" assert_success - run grep -q "'fallback_module': ovos_config_speech_stt_fallback" "$speech_tasks" + run grep -q "ovos_config_speech_stt_memory_budget_mb" "$speech_tasks" assert_success - run grep -q "speech_prefetch.py" "$speech_tasks" + run grep -q "'fallback_module': ovos_config_speech_stt_fallback" "$speech_tasks" assert_success run grep -q "^ovos_config_speech_stt_fallback: ovos-stt-plugin-server$" ansible/roles/ovos_config/defaults/main.yml assert_success From 0a00e198d50ccb72f277a992022e9c7bc7eac4cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Tue, 6 Oct 2026 17:20:31 -0400 Subject: [PATCH 05/14] Run local speech in containers once ovos-docker can Container installs now get the speech choice too, for the ovos and listener profiles (a containers satellite runs hivemind-docker images, which carry no speech plugins). They pass --offline or --online to ovos-config autoconfigure, so public now means both public there as well, instead of autoconfigure's hybrid mode. Local speech raises LISTENER_MEMORY_LIMIT and AUDIO_MEMORY_LIMIT, then runs the same speech tasks as the virtualenv inside the cli, listener and audio containers, each container fetching its own model into its own volume. It only goes local when the compose files in use read LISTENER_MEMORY_LIMIT and mount ovos_stt_models, and the pulled listener and audio images carry onnx-asr and phoonnx (OpenVoiceOS/ovos-docker#200). Until the pinned release has both, containers stay public and the installer says so, so this is safe to merge before that release. Co-Authored-By: Claude Opus 5.5 --- ansible/roles/ovos_config/tasks/speech.yml | 50 +++++++++-- .../roles/ovos_containers/defaults/main.yml | 7 ++ .../roles/ovos_containers/tasks/composer.yml | 89 +++++++++++++++++++ .../ovos_containers/templates/docker/env.j2 | 6 ++ .../roles/ovos_installer/defaults/main.yml | 2 +- ansible/roles/ovos_installer/tasks/assert.yml | 6 +- ansible/roles/ovos_virtualenv/tasks/venv.yml | 3 + docs/automation.md | 2 +- tests/bats/code_quality.bats | 40 ++++++++- tests/bats/speech.bats | 34 +++++-- tests/bats/tui_navigation.bats | 13 +++ translations/ca-es/strings.json | 2 +- translations/da/strings.json | 2 +- translations/de-de/strings.json | 2 +- translations/en-us/strings.json | 2 +- translations/es-es/strings.json | 2 +- translations/eu-es/strings.json | 2 +- translations/fr-fr/strings.json | 2 +- translations/gl-es/strings.json | 2 +- translations/hi-in/strings.json | 2 +- translations/it-it/strings.json | 2 +- translations/kab-dz/strings.json | 2 +- translations/nl-nl/strings.json | 2 +- translations/pl-pl/strings.json | 2 +- translations/pt-pt/strings.json | 2 +- tui/locales/ca-es/summary.sh | 2 +- tui/locales/da/summary.sh | 2 +- tui/locales/de-de/summary.sh | 2 +- tui/locales/en-us/summary.sh | 2 +- tui/locales/es-es/summary.sh | 2 +- tui/locales/eu-es/summary.sh | 2 +- tui/locales/fr-fr/summary.sh | 2 +- tui/locales/gl-es/summary.sh | 2 +- tui/locales/hi-in/summary.sh | 2 +- tui/locales/it-it/summary.sh | 2 +- tui/locales/kab-dz/summary.sh | 2 +- tui/locales/nl-nl/summary.sh | 2 +- tui/locales/pl-pl/summary.sh | 2 +- tui/locales/pt-pt/summary.sh | 2 +- utils/speech.sh | 21 +++-- 40 files changed, 278 insertions(+), 51 deletions(-) diff --git a/ansible/roles/ovos_config/tasks/speech.yml b/ansible/roles/ovos_config/tasks/speech.yml index 28f57157..d825589f 100644 --- a/ansible/roles/ovos_config/tasks/speech.yml +++ b/ansible/roles/ovos_config/tasks/speech.yml @@ -8,7 +8,9 @@ # per-language defaults for units, wake words and intents, which this installer # sets itself. # -# The caller passes ovos_config_speech_python, the OVOS virtualenv's Python. +# The caller says where OVOS runs: ovos_config_speech_python, the virtualenv's +# Python, or ovos_config_speech_containers, the names of the listener and audio +# containers. - name: Pick, download and check the local speech plugins become: true become_user: "{{ ovos_installer_user }}" @@ -23,20 +25,56 @@ - "{{ ovos_config_speech_stt_memory_budget_mb }}" register: ovos_config_speech_setup changed_when: false + when: ovos_config_speech_containers is not defined + +# Each half is set up where it will run - the recognizer in the listener, the +# voice in the audio service - so each model lands in that container's volume +# (ovos_stt_models, ovos_tts_models). +- name: Pick, download and check the local speech plugins in their containers + become: true + become_user: "{{ ovos_installer_user }}" + community.docker.docker_container_exec: + container: "{{ ovos_config_speech_containers[item.container] }}" + argv: + - python3 + - -c + - "{{ lookup('ansible.builtin.file', 'speech_setup.py') }}" + - "{{ item.kind }}" + - "{{ ovos_installer_locale }}" + - "{{ 'female' if ovos_installer_ovos_config_tts_gender == '--female' else 'male' }}" + - "{{ ovos_config_speech_stt_memory_budget_mb }}" + loop: + - { kind: stt, container: listener } + - { kind: tts, container: audio } + loop_control: + label: "{{ item.kind }}" + register: ovos_config_speech_setup_containers + changed_when: false + # The listener and audio containers only exist in the desktop profiles. + when: + - ovos_config_speech_containers is defined + - ovos_containers_is_desktop_profile | bool # A failed local recognition, or one that hears nothing, goes to the public # server for that utterance instead of leaving the user unanswered. - name: Keep what works on this machine vars: - _setup: "{{ ovos_config_speech_setup.stdout | from_json }}" + _results: >- + {{ + (ovos_config_speech_setup_containers.results | map(attribute='stdout') | map('from_json') | list) + if ovos_config_speech_containers is defined + else [ovos_config_speech_setup.stdout | from_json] + }} + _stt: "{{ _results | map(attribute='stt') | select | list | first | default(none) }}" + _tts: "{{ _results | map(attribute='tts') | select | list | first | default(none) }}" ansible.builtin.set_fact: ovos_config_speech_wanted: >- {{ - ({'stt': _setup.stt | combine({'fallback_module': ovos_config_speech_stt_fallback})} - if _setup.stt else {}) - | combine({'tts': _setup.tts} if _setup.tts else {}) + ({'stt': _stt | combine({'fallback_module': ovos_config_speech_stt_fallback})} + if _stt else {}) + | combine({'tts': _tts} if _tts else {}) }} - ovos_config_speech_notes: "{{ _setup.notes | default([]) }}" + ovos_config_speech_notes: "{{ _results | map(attribute='notes') | flatten }}" - name: Report what the local speech setup had to work around ansible.builtin.debug: diff --git a/ansible/roles/ovos_containers/defaults/main.yml b/ansible/roles/ovos_containers/defaults/main.yml index 5d87ae25..7aec356d 100644 --- a/ansible/roles/ovos_containers/defaults/main.yml +++ b/ansible/roles/ovos_containers/defaults/main.yml @@ -181,6 +181,13 @@ ovos_containers_hivemind_client_ids: >- {{ ovos_containers_hivemind_clients.stdout_lines | default([]) | select('match', '^[0-9]+,') | map('split', ',') | map('first') | list }} ovos_containers_container_ovos_cli: ovos_cli +ovos_containers_container_ovos_listener: ovos_listener +ovos_containers_container_ovos_audio: ovos_audio +# Local speech (ovos_installer_speech_engine: local) raises the limits ovos-docker +# gives the listener and the audio service: onnx-asr's recognition model alone +# peaks around 1.2 GB, and some languages get larger models than that. +ovos_containers_speech_listener_memory_limit: "{{ ovos_installer_speech_listener_memory_limit | default('4G') }}" +ovos_containers_speech_audio_memory_limit: "{{ ovos_installer_speech_audio_memory_limit | default('1G') }}" # Service and container names from ovos-docker's docker-compose.skills-extra.yml. # Keep them overridable in case upstream changes. ovos_containers_service_skill_homeassistant: ovos_skill_homeassistant diff --git a/ansible/roles/ovos_containers/tasks/composer.yml b/ansible/roles/ovos_containers/tasks/composer.yml index 16768c3c..cd48ac8b 100644 --- a/ansible/roles/ovos_containers/tasks/composer.yml +++ b/ansible/roles/ovos_containers/tasks/composer.yml @@ -60,6 +60,80 @@ - not ansible_check_mode - ovos_installer_profile == "satellite" +# Local speech needs two things from ovos-docker, and both are checked rather than +# assumed. The compose files of the pinned release have to give the listener room +# for the recognition model and a volume to keep it in - with less, the model gets +# the listener OOM-killed - and the images this stack just pulled have to carry the +# onnx-asr and phoonnx plugins, or the listener cannot load its recognizer. Short +# of either, speech stays on the public servers. +- name: Read the compose files this stack runs + ansible.builtin.slurp: + src: "{{ ovos_containers_composition_directory }}/{{ item }}" + loop: "{{ ovos_containers_composition_files }}" + register: ovos_containers_composition_contents + when: + - (ovos_installer_speech_engine | default('public')) == 'local' + - ovos_containers_is_desktop_profile | bool + +- name: Check the listener and audio images carry the local speech plugins + become: true + become_user: "{{ ovos_installer_user }}" + community.docker.docker_container_exec: + container: "{{ item.container }}" + argv: + - python3 + - -c + - "{{ item.probe }}" + loop: + - container: "{{ ovos_containers_container_ovos_listener }}" + probe: >- + from ovos_plugin_manager.stt import find_stt_plugins; + raise SystemExit(0 if 'ovos-stt-plugin-onnx-asr' in find_stt_plugins() else 1) + - container: "{{ ovos_containers_container_ovos_audio }}" + probe: >- + from ovos_plugin_manager.tts import find_tts_plugins; + raise SystemExit(0 if 'ovos-tts-plugin-phoonnx' in find_tts_plugins() else 1) + loop_control: + label: "{{ item.container }}" + register: ovos_containers_speech_plugins + failed_when: false + changed_when: false + when: + - not ansible_check_mode + - (ovos_installer_speech_engine | default('public')) == 'local' + - ovos_containers_is_desktop_profile | bool + +- name: Decide where speech runs + vars: + _compose: >- + {{ ovos_containers_composition_contents.results | default([]) + | map(attribute='content', default='') | map('b64decode') | join('\n') }} + _compose_ready: "{{ 'LISTENER_MEMORY_LIMIT' in _compose and 'ovos_stt_models' in _compose }}" + _plugins_ready: >- + {{ (ovos_containers_speech_plugins.results | default([]) | length) == 2 + and (ovos_containers_speech_plugins.results | map(attribute='rc', default=1) | max) == 0 }} + ansible.builtin.set_fact: + ovos_containers_speech_engine: >- + {{ + 'local' + if (ovos_installer_speech_engine | default('public')) == 'local' + and _compose_ready | bool and _plugins_ready | bool + else 'public' + }} + +- name: Say why speech stays on the public servers + ansible.builtin.debug: + msg: >- + The ovos-docker release this installer runs, or the images it pulled, cannot + run speech on the device yet, so speech stays on the public servers. Re-run + the installer once they can. + when: + - (ovos_installer_speech_engine | default('public')) == 'local' + - ovos_containers_speech_engine == 'public' + +# --offline or --online, never neither: without one autoconfigure picks its hybrid +# mode (voice on the device, recognition on the public servers), which is neither +# of the two choices the installer offers. - name: Merge ovos-config language recommendations into mounted config become: true become_user: "{{ ovos_installer_user }}" @@ -67,6 +141,7 @@ container: "{{ ovos_containers_container_ovos_cli }}" command: >- ovos-config autoconfigure --lang {{ ovos_installer_locale }} + {{ '--offline' if ovos_containers_speech_engine == 'local' else '--online' }} {{ ovos_installer_ovos_config_tts_gender }} when: - not ansible_check_mode @@ -77,6 +152,20 @@ # container ovos_cli" after the stack is already up. - ovos_containers_is_desktop_profile | bool +- name: Configure local speech + ansible.builtin.include_role: + name: ovos_config + tasks_from: speech.yml + vars: + ovos_config_speech_containers: + cli: "{{ ovos_containers_container_ovos_cli }}" + listener: "{{ ovos_containers_container_ovos_listener }}" + audio: "{{ ovos_containers_container_ovos_audio }}" + when: + - not ansible_check_mode + - ovos_containers_speech_engine == 'local' + - ovos_containers_is_desktop_profile | bool + - name: Compute optional skills flags vars: _ovos_homeassistant_url: >- diff --git a/ansible/roles/ovos_containers/templates/docker/env.j2 b/ansible/roles/ovos_containers/templates/docker/env.j2 index 972ae65e..cbcc6473 100644 --- a/ansible/roles/ovos_containers/templates/docker/env.j2 +++ b/ansible/roles/ovos_containers/templates/docker/env.j2 @@ -12,6 +12,9 @@ '{{ text }}' {%- endif -%} {%- endmacro -%} +{% if (ovos_installer_speech_engine | default('public')) == 'local' %} +AUDIO_MEMORY_LIMIT={{ env(ovos_containers_speech_audio_memory_limit) }} +{% endif %} {% if ovos_installer_display_server != 'N/A' %} DISPLAY={{ env(ovos_installer_display_server_number if ovos_installer_display_server_number is defined else ovos_containers_display_default) }} {% endif %} @@ -30,6 +33,9 @@ I2C_GID={{ env(getent_group['i2c'][1]) }} {% if 'input' in getent_group %} INPUT_GID={{ env(getent_group['input'][1]) }} {% endif %} +{% if (ovos_installer_speech_engine | default('public')) == 'local' %} +LISTENER_MEMORY_LIMIT={{ env(ovos_containers_speech_listener_memory_limit) }} +{% endif %} OVOS_CONFIG_FOLDER={{ env(ovos_containers_ovos_config_dir) }} OVOS_CONFIG_PHAL_FOLDER={{ env(ovos_containers_ovos_config_phal_dir) }} OVOS_PERSONA_FOLDER={{ env(ovos_containers_ovos_persona_dir) }} diff --git a/ansible/roles/ovos_installer/defaults/main.yml b/ansible/roles/ovos_installer/defaults/main.yml index dbd38f00..b05cc16f 100644 --- a/ansible/roles/ovos_installer/defaults/main.yml +++ b/ansible/roles/ovos_installer/defaults/main.yml @@ -157,7 +157,7 @@ ovos_installer_tuning_source_volume: 1.0 ovos_installer_cpu_is_capable: false # Where STT and TTS run: "public" (the Open Voice OS servers, the default) or # "local" (on this host, see ovos_config/tasks/speech.yml). setup.sh only passes -# local for alpha virtualenv installs on hardware that can run it. +# local for alpha installs on hardware that can run it. ovos_installer_speech_engine: public ovos_installer_local_speech_capable: false ovos_installer_locale: "en-us" diff --git a/ansible/roles/ovos_installer/tasks/assert.yml b/ansible/roles/ovos_installer/tasks/assert.yml index 2c5bd2a0..4d7fbfb1 100644 --- a/ansible/roles/ovos_installer/tasks/assert.yml +++ b/ansible/roles/ovos_installer/tasks/assert.yml @@ -40,10 +40,12 @@ - name: Assert local speech constraints ansible.builtin.assert: that: - - ovos_installer_method == 'virtualenv' - ovos_installer_channel == 'alpha' - ovos_installer_profile != 'server' - fail_msg: "Local speech is only available on alpha virtualenv installs whose profile has audio." + - ovos_installer_method == 'virtualenv' or ovos_installer_profile in ['ovos', 'listener'] + fail_msg: >- + Local speech is only available on alpha installs whose profile has audio, + and with containers only for the ovos and listener profiles. when: - not (ovos_installer_is_cleaning | bool) - ovos_installer_speech_engine == 'local' diff --git a/ansible/roles/ovos_virtualenv/tasks/venv.yml b/ansible/roles/ovos_virtualenv/tasks/venv.yml index 3cecf75b..e031a00f 100644 --- a/ansible/roles/ovos_virtualenv/tasks/venv.yml +++ b/ansible/roles/ovos_virtualenv/tasks/venv.yml @@ -516,6 +516,9 @@ - autoconfigure - --lang - "{{ ovos_installer_locale }}" + # Never neither: without a mode autoconfigure picks its hybrid one, which is + # not one of the two choices the installer offers. + - "{{ '--offline' if (ovos_installer_speech_engine | default('public')) == 'local' else '--online' }}" - "{{ ovos_installer_ovos_config_tts_gender }}" environment: PATH: "{{ ovos_virtualenv_path }}/bin" diff --git a/docs/automation.md b/docs/automation.md index 16d50a40..896efa0e 100644 --- a/docs/automation.md +++ b/docs/automation.md @@ -46,7 +46,7 @@ including one that runs speech recognition and the voice on a Raspberry Pi 5 | `llm.key` | API key for that endpoint, required with `features.llm` | | `llm.model` | Model name to use, required with `features.llm` | | `llm.persona` | System prompt for `ovos-persona`, required with `features.llm` | -| `speech_engine` | Where speech recognition and the voice run: `public` (the default) uses the community's public servers, which run on goodwill rather than as a production service and can go offline at any time; `local` runs them on the device. Local needs the `alpha` channel, the `virtualenv` method, a profile with audio and a Raspberry Pi 5 with 8 GB or an equivalent machine; anywhere else the installer says so and uses `public` | +| `speech_engine` | Where speech recognition and the voice run: `public` (the default) uses the community's public servers, which run on goodwill rather than as a production service and can go offline at any time; `local` runs them on the device. Local needs the `alpha` channel, a profile with audio (with `containers`, `ovos` or `listener`) and a Raspberry Pi 5 with 8 GB or an equivalent machine; anywhere else the installer says so and uses `public` | | `raspberry_pi_tuning` | Maximum-performance tuning for a Pi, including an overclocking prompt | | `share_telemetry` | Share anonymous usage statistics — see [Telemetry](telemetry.md) | | `share_usage_telemetry` | Share detailed usage data — see [Telemetry](telemetry.md) | diff --git a/tests/bats/code_quality.bats b/tests/bats/code_quality.bats index f2442ea9..87ed4d77 100644 --- a/tests/bats/code_quality.bats +++ b/tests/bats/code_quality.bats @@ -2699,8 +2699,14 @@ YAML run grep -F -q "{{ ovos_installer_ovos_config_tts_gender }}" "$container_tasks" assert_success - run bash -c "awk '/ovos-config autoconfigure --lang/ { in_command=1 } in_command { print } in_command && /ovos_installer_ovos_config_tts_gender/ { exit }' \"$container_tasks\" | grep -F -q -- '--online'" - assert_failure + # Always a mode: with neither flag autoconfigure goes hybrid (the voice on the + # device, recognition on the public servers), which is not one of the two + # speech choices the installer offers. + run grep -F -q "{{ '--offline' if ovos_containers_speech_engine == 'local' else '--online' }}" "$container_tasks" + assert_success + + run grep -F -q "{{ '--offline' if (ovos_installer_speech_engine | default('public')) == 'local' else '--online' }}" "$virtualenv_tasks" + assert_success run grep -F -q "ovos_installer_ovos_config_autoconfigure_enabled | default(false) | bool" "$container_tasks" assert_success @@ -5186,3 +5192,33 @@ for template in ('core-requirements.txt.j2', 'satellite-requirements.txt.j2'): assert_success [ -x .github/scripts/assert_local_speech.sh ] } + +@test "local_speech_reaches_containers_only_when_ovos_docker_can_run_it" { + local composer="ansible/roles/ovos_containers/tasks/composer.yml" + + # The compose of the pinned release must give the listener room for the model and a + # volume to keep it in, and the pulled images must carry both plugins; short of + # either, containers stay public instead of OOM-killing or crash-looping the listener. + run grep -F -q "'LISTENER_MEMORY_LIMIT' in _compose and 'ovos_stt_models' in _compose" "$composer" + assert_success + run grep -F -q "'ovos-stt-plugin-onnx-asr' in find_stt_plugins()" "$composer" + assert_success + run grep -F -q "'ovos-tts-plugin-phoonnx' in find_tts_plugins()" "$composer" + assert_success + + # Local speech is configured through the same tasks as the virtualenv, run in the + # cli, listener and audio containers. + run bash -c "awk '/- name: Configure local speech/,/- name: Compute optional skills flags/' '$composer' | grep -q 'ovos_config_speech_containers:'" + assert_success + run grep -q "^ovos_containers_container_ovos_listener: ovos_listener$" ansible/roles/ovos_containers/defaults/main.yml + assert_success + run grep -q "^ovos_containers_container_ovos_audio: ovos_audio$" ansible/roles/ovos_containers/defaults/main.yml + assert_success + + # The raised limits only go into .env when local speech was asked for. + local env_template="ansible/roles/ovos_containers/templates/docker/env.j2" + run bash -c "grep -B1 '^LISTENER_MEMORY_LIMIT=' '$env_template' | grep -q \"ovos_installer_speech_engine | default('public')) == 'local'\"" + assert_success + run bash -c "grep -B1 '^AUDIO_MEMORY_LIMIT=' '$env_template' | grep -q \"ovos_installer_speech_engine | default('public')) == 'local'\"" + assert_success +} diff --git a/tests/bats/speech.bats b/tests/bats/speech.bats index b7497e95..18a34d55 100644 --- a/tests/bats/speech.bats +++ b/tests/bats/speech.bats @@ -150,7 +150,7 @@ function teardown() { assert_equal "$LOCAL_SPEECH_CAPABLE" "false" } -@test "speech: local is only available on alpha virtualenv installs with audio" { +@test "speech: local is only available on alpha installs with audio" { LOCAL_SPEECH_CAPABLE="true" CHANNEL="alpha" METHOD="virtualenv" @@ -172,10 +172,6 @@ function teardown() { assert_failure CHANNEL="alpha" - METHOD="containers" - run local_speech_available - assert_failure - METHOD="virtualenv" LOCAL_SPEECH_CAPABLE="false" run local_speech_available @@ -268,3 +264,31 @@ function run_scenario_with_speech_engine() { assert_success assert_output "unset|true|speech_engine: offline (expected: local, public)" } + +@test "speech: containers get local speech for the ovos and listener profiles only" { + LOCAL_SPEECH_CAPABLE="true" + CHANNEL="alpha" + METHOD="containers" + + PROFILE="ovos" + run local_speech_available + assert_success + + PROFILE="listener" + run local_speech_available + assert_success + + # A containers satellite runs hivemind-docker, whose images carry no speech plugins. + PROFILE="satellite" + run local_speech_available + assert_failure + + PROFILE="server" + run local_speech_available + assert_failure + + PROFILE="ovos" + CHANNEL="testing" + run local_speech_available + assert_failure +} diff --git a/tests/bats/tui_navigation.bats b/tests/bats/tui_navigation.bats index 382498b7..873c395d 100644 --- a/tests/bats/tui_navigation.bats +++ b/tests/bats/tui_navigation.bats @@ -776,3 +776,16 @@ function rendered_titles() { run rendered_titles refute_line "Open Voice OS Installation - Speech" } + +@test "tui: a containers install on alpha is asked where speech runs" { + export LOCAL_SPEECH_CAPABLE="true" + WHIPTAIL_PREFERRED_TAGS="containers alpha" + + # shellcheck source=tui/main.sh + source tui/main.sh + + assert_equal "$METHOD" "containers" + run rendered_titles + assert_line --index 6 "Open Voice OS Installation - Speech" + assert_equal "$SPEECH_ENGINE" "local" +} diff --git a/translations/ca-es/strings.json b/translations/ca-es/strings.json index c9fe0e9c..df172ace 100644 --- a/translations/ca-es/strings.json +++ b/translations/ca-es/strings.json @@ -68,7 +68,7 @@ "speech_local": "en aquest equip", "speech_public": "servidors públics", "speech_public_hardware": "servidors públics (local: Pi 5 de 8 GB o més)", - "speech_public_setup": "servidors públics (local: alpha i virtualenv)", + "speech_public_setup": "servidors públics (local no disponible aquí)", "speech_unused": "aquest perfil no la fa servir" }, "telemetry.sh": { diff --git a/translations/da/strings.json b/translations/da/strings.json index 3d5afac7..f9775956 100644 --- a/translations/da/strings.json +++ b/translations/da/strings.json @@ -68,7 +68,7 @@ "speech_local": "på denne maskine", "speech_public": "offentlige servere", "speech_public_hardware": "offentlige servere (lokal: Pi 5 med 8 GB eller mere)", - "speech_public_setup": "offentlige servere (lokal: alpha og virtualenv)", + "speech_public_setup": "offentlige servere (lokal er ikke tilgængelig her)", "speech_unused": "bruges ikke af denne profil" }, "telemetry.sh": { diff --git a/translations/de-de/strings.json b/translations/de-de/strings.json index b5c48e09..15a836b9 100644 --- a/translations/de-de/strings.json +++ b/translations/de-de/strings.json @@ -68,7 +68,7 @@ "speech_local": "auf diesem Rechner", "speech_public": "öffentliche Server", "speech_public_hardware": "öffentliche Server (lokal: Pi 5 mit 8 GB oder besser)", - "speech_public_setup": "öffentliche Server (lokal: alpha und virtualenv)", + "speech_public_setup": "öffentliche Server (lokal hier nicht verfügbar)", "speech_unused": "von diesem Profil nicht genutzt" }, "telemetry.sh": { diff --git a/translations/en-us/strings.json b/translations/en-us/strings.json index 2f2d9afd..e4169969 100644 --- a/translations/en-us/strings.json +++ b/translations/en-us/strings.json @@ -73,7 +73,7 @@ "speech_local": "on this machine", "speech_public": "public servers", "speech_public_hardware": "public servers (local needs a Pi 5 with 8 GB or better)", - "speech_public_setup": "public servers (local needs alpha and virtualenv)", + "speech_public_setup": "public servers (local is not available here)", "speech_unused": "not used by this profile" }, "telemetry.sh": { diff --git a/translations/es-es/strings.json b/translations/es-es/strings.json index 82f8519b..4a4303e8 100644 --- a/translations/es-es/strings.json +++ b/translations/es-es/strings.json @@ -68,7 +68,7 @@ "speech_local": "en este equipo", "speech_public": "servidores públicos", "speech_public_hardware": "servidores públicos (local: Pi 5 con 8 GB o más)", - "speech_public_setup": "servidores públicos (local: alpha y virtualenv)", + "speech_public_setup": "servidores públicos (local no disponible aquí)", "speech_unused": "este perfil no la usa" }, "telemetry.sh": { diff --git a/translations/eu-es/strings.json b/translations/eu-es/strings.json index 7017cc99..090bea9b 100644 --- a/translations/eu-es/strings.json +++ b/translations/eu-es/strings.json @@ -67,7 +67,7 @@ "speech_local": "makina honetan", "speech_public": "zerbitzari publikoak", "speech_public_hardware": "zerbitzari publikoak (lokala: 8 GB-ko Pi 5 edo hobea)", - "speech_public_setup": "zerbitzari publikoak (lokala: alpha eta virtualenv)", + "speech_public_setup": "zerbitzari publikoak (lokala ez dago hemen)", "speech_unused": "profil honek ez du erabiltzen" }, "telemetry.sh": { diff --git a/translations/fr-fr/strings.json b/translations/fr-fr/strings.json index ae9ca07e..740512d8 100644 --- a/translations/fr-fr/strings.json +++ b/translations/fr-fr/strings.json @@ -68,7 +68,7 @@ "speech_local": "sur cette machine", "speech_public": "serveurs publics", "speech_public_hardware": "serveurs publics (local : Pi 5 8 Go minimum)", - "speech_public_setup": "serveurs publics (local : alpha et virtualenv)", + "speech_public_setup": "serveurs publics (local indisponible ici)", "speech_unused": "non utilisé par ce profil" }, "telemetry.sh": { diff --git a/translations/gl-es/strings.json b/translations/gl-es/strings.json index 0c0b1594..ea9dacb2 100644 --- a/translations/gl-es/strings.json +++ b/translations/gl-es/strings.json @@ -68,7 +68,7 @@ "speech_local": "neste equipo", "speech_public": "servidores públicos", "speech_public_hardware": "servidores públicos (local: Pi 5 de 8 GB ou mellor)", - "speech_public_setup": "servidores públicos (local: alpha e virtualenv)", + "speech_public_setup": "servidores públicos (local non dispoñible aquí)", "speech_unused": "este perfil non a usa" }, "telemetry.sh": { diff --git a/translations/hi-in/strings.json b/translations/hi-in/strings.json index 8f3dfcb5..e000d0f3 100644 --- a/translations/hi-in/strings.json +++ b/translations/hi-in/strings.json @@ -67,7 +67,7 @@ "speech_local": "इसी मशीन पर", "speech_public": "सार्वजनिक सर्वर", "speech_public_hardware": "सार्वजनिक सर्वर (लोकल: 8 GB वाला Pi 5 या बेहतर)", - "speech_public_setup": "सार्वजनिक सर्वर (लोकल: alpha और virtualenv)", + "speech_public_setup": "सार्वजनिक सर्वर (यहाँ लोकल उपलब्ध नहीं)", "speech_unused": "यह प्रोफ़ाइल इसका उपयोग नहीं करती" }, "telemetry.sh": { diff --git a/translations/it-it/strings.json b/translations/it-it/strings.json index d1a2dd16..22746fb3 100644 --- a/translations/it-it/strings.json +++ b/translations/it-it/strings.json @@ -68,7 +68,7 @@ "speech_local": "su questa macchina", "speech_public": "server pubblici", "speech_public_hardware": "server pubblici (locale: Pi 5 con 8 GB o superiore)", - "speech_public_setup": "server pubblici (locale: alpha e virtualenv)", + "speech_public_setup": "server pubblici (locale non disponibile qui)", "speech_unused": "non usata da questo profilo" }, "telemetry.sh": { diff --git a/translations/kab-dz/strings.json b/translations/kab-dz/strings.json index 543c92d8..f9c2875e 100644 --- a/translations/kab-dz/strings.json +++ b/translations/kab-dz/strings.json @@ -71,7 +71,7 @@ "speech_local": "ɣef tmacint-a", "speech_public": "iqeddacen izayazen", "speech_public_hardware": "iqeddacen izayazen (adigan: Pi 5 s 8 GB neɣ ugar)", - "speech_public_setup": "iqeddacen izayazen (adigan: alpha d virtualenv)", + "speech_public_setup": "iqeddacen izayazen (adigan ulac-it dagi)", "speech_unused": "ur yettwaseqdac ara deg umaɣnu-a" }, "telemetry.sh": { diff --git a/translations/nl-nl/strings.json b/translations/nl-nl/strings.json index b6caf68d..6042fcf6 100644 --- a/translations/nl-nl/strings.json +++ b/translations/nl-nl/strings.json @@ -68,7 +68,7 @@ "speech_local": "op deze machine", "speech_public": "openbare servers", "speech_public_hardware": "openbare servers (lokaal: Pi 5 met 8 GB of beter)", - "speech_public_setup": "openbare servers (lokaal: alpha en virtualenv)", + "speech_public_setup": "openbare servers (lokaal hier niet beschikbaar)", "speech_unused": "niet gebruikt door dit profiel" }, "telemetry.sh": { diff --git a/translations/pl-pl/strings.json b/translations/pl-pl/strings.json index 8f7532cf..6c8e51cf 100644 --- a/translations/pl-pl/strings.json +++ b/translations/pl-pl/strings.json @@ -68,7 +68,7 @@ "speech_local": "na tym komputerze", "speech_public": "serwery publiczne", "speech_public_hardware": "serwery publiczne (lokalnie: Pi 5 z 8 GB lub lepszy)", - "speech_public_setup": "serwery publiczne (lokalnie: alpha i virtualenv)", + "speech_public_setup": "serwery publiczne (tryb lokalny tu niedostępny)", "speech_unused": "nieużywana w tym profilu" }, "telemetry.sh": { diff --git a/translations/pt-pt/strings.json b/translations/pt-pt/strings.json index cd52485a..a9dcadf0 100644 --- a/translations/pt-pt/strings.json +++ b/translations/pt-pt/strings.json @@ -68,7 +68,7 @@ "speech_local": "nesta máquina", "speech_public": "servidores públicos", "speech_public_hardware": "servidores públicos (local: Pi 5 com 8 GB ou melhor)", - "speech_public_setup": "servidores públicos (local: alpha e virtualenv)", + "speech_public_setup": "servidores públicos (local indisponível aqui)", "speech_unused": "não utilizado por este perfil" }, "telemetry.sh": { diff --git a/tui/locales/ca-es/summary.sh b/tui/locales/ca-es/summary.sh index fecec480..eeb41823 100644 --- a/tui/locales/ca-es/summary.sh +++ b/tui/locales/ca-es/summary.sh @@ -26,7 +26,7 @@ SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be sk SUMMARY_SPEECH_LOCAL="en aquest equip" SUMMARY_SPEECH_PUBLIC="servidors públics" SUMMARY_SPEECH_PUBLIC_HARDWARE="servidors públics (local: Pi 5 de 8 GB o més)" -SUMMARY_SPEECH_PUBLIC_SETUP="servidors públics (local: alpha i virtualenv)" +SUMMARY_SPEECH_PUBLIC_SETUP="servidors públics (local no disponible aquí)" SUMMARY_SPEECH_UNUSED="aquest perfil no la fa servir" export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/da/summary.sh b/tui/locales/da/summary.sh index 4d762461..a9754ee4 100644 --- a/tui/locales/da/summary.sh +++ b/tui/locales/da/summary.sh @@ -26,7 +26,7 @@ SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be sk SUMMARY_SPEECH_LOCAL="på denne maskine" SUMMARY_SPEECH_PUBLIC="offentlige servere" SUMMARY_SPEECH_PUBLIC_HARDWARE="offentlige servere (lokal: Pi 5 med 8 GB eller mere)" -SUMMARY_SPEECH_PUBLIC_SETUP="offentlige servere (lokal: alpha og virtualenv)" +SUMMARY_SPEECH_PUBLIC_SETUP="offentlige servere (lokal er ikke tilgængelig her)" SUMMARY_SPEECH_UNUSED="bruges ikke af denne profil" export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/de-de/summary.sh b/tui/locales/de-de/summary.sh index ce6b71f4..87f7d22b 100644 --- a/tui/locales/de-de/summary.sh +++ b/tui/locales/de-de/summary.sh @@ -26,7 +26,7 @@ SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be sk SUMMARY_SPEECH_LOCAL="auf diesem Rechner" SUMMARY_SPEECH_PUBLIC="öffentliche Server" SUMMARY_SPEECH_PUBLIC_HARDWARE="öffentliche Server (lokal: Pi 5 mit 8 GB oder besser)" -SUMMARY_SPEECH_PUBLIC_SETUP="öffentliche Server (lokal: alpha und virtualenv)" +SUMMARY_SPEECH_PUBLIC_SETUP="öffentliche Server (lokal hier nicht verfügbar)" SUMMARY_SPEECH_UNUSED="von diesem Profil nicht genutzt" export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/en-us/summary.sh b/tui/locales/en-us/summary.sh index 07c25363..3bd5b789 100644 --- a/tui/locales/en-us/summary.sh +++ b/tui/locales/en-us/summary.sh @@ -26,7 +26,7 @@ SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be sk SUMMARY_SPEECH_LOCAL="on this machine" SUMMARY_SPEECH_PUBLIC="public servers" SUMMARY_SPEECH_PUBLIC_HARDWARE="public servers (local needs a Pi 5 with 8 GB or better)" -SUMMARY_SPEECH_PUBLIC_SETUP="public servers (local needs alpha and virtualenv)" +SUMMARY_SPEECH_PUBLIC_SETUP="public servers (local is not available here)" SUMMARY_SPEECH_UNUSED="not used by this profile" export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/es-es/summary.sh b/tui/locales/es-es/summary.sh index c2e08d1e..18dadf7d 100644 --- a/tui/locales/es-es/summary.sh +++ b/tui/locales/es-es/summary.sh @@ -26,7 +26,7 @@ SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be sk SUMMARY_SPEECH_LOCAL="en este equipo" SUMMARY_SPEECH_PUBLIC="servidores públicos" SUMMARY_SPEECH_PUBLIC_HARDWARE="servidores públicos (local: Pi 5 con 8 GB o más)" -SUMMARY_SPEECH_PUBLIC_SETUP="servidores públicos (local: alpha y virtualenv)" +SUMMARY_SPEECH_PUBLIC_SETUP="servidores públicos (local no disponible aquí)" SUMMARY_SPEECH_UNUSED="este perfil no la usa" export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/eu-es/summary.sh b/tui/locales/eu-es/summary.sh index 76a7db19..eb616ae8 100644 --- a/tui/locales/eu-es/summary.sh +++ b/tui/locales/eu-es/summary.sh @@ -26,7 +26,7 @@ SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be sk SUMMARY_SPEECH_LOCAL="makina honetan" SUMMARY_SPEECH_PUBLIC="zerbitzari publikoak" SUMMARY_SPEECH_PUBLIC_HARDWARE="zerbitzari publikoak (lokala: 8 GB-ko Pi 5 edo hobea)" -SUMMARY_SPEECH_PUBLIC_SETUP="zerbitzari publikoak (lokala: alpha eta virtualenv)" +SUMMARY_SPEECH_PUBLIC_SETUP="zerbitzari publikoak (lokala ez dago hemen)" SUMMARY_SPEECH_UNUSED="profil honek ez du erabiltzen" export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/fr-fr/summary.sh b/tui/locales/fr-fr/summary.sh index 771ff81b..34e8bacd 100644 --- a/tui/locales/fr-fr/summary.sh +++ b/tui/locales/fr-fr/summary.sh @@ -26,7 +26,7 @@ SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be sk SUMMARY_SPEECH_LOCAL="sur cette machine" SUMMARY_SPEECH_PUBLIC="serveurs publics" SUMMARY_SPEECH_PUBLIC_HARDWARE="serveurs publics (local : Pi 5 8 Go minimum)" -SUMMARY_SPEECH_PUBLIC_SETUP="serveurs publics (local : alpha et virtualenv)" +SUMMARY_SPEECH_PUBLIC_SETUP="serveurs publics (local indisponible ici)" SUMMARY_SPEECH_UNUSED="non utilisé par ce profil" export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/gl-es/summary.sh b/tui/locales/gl-es/summary.sh index e9540284..f520b69a 100644 --- a/tui/locales/gl-es/summary.sh +++ b/tui/locales/gl-es/summary.sh @@ -26,7 +26,7 @@ SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be sk SUMMARY_SPEECH_LOCAL="neste equipo" SUMMARY_SPEECH_PUBLIC="servidores públicos" SUMMARY_SPEECH_PUBLIC_HARDWARE="servidores públicos (local: Pi 5 de 8 GB ou mellor)" -SUMMARY_SPEECH_PUBLIC_SETUP="servidores públicos (local: alpha e virtualenv)" +SUMMARY_SPEECH_PUBLIC_SETUP="servidores públicos (local non dispoñible aquí)" SUMMARY_SPEECH_UNUSED="este perfil non a usa" export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/hi-in/summary.sh b/tui/locales/hi-in/summary.sh index 58685700..c3be8234 100644 --- a/tui/locales/hi-in/summary.sh +++ b/tui/locales/hi-in/summary.sh @@ -24,7 +24,7 @@ SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be sk SUMMARY_SPEECH_LOCAL="इसी मशीन पर" SUMMARY_SPEECH_PUBLIC="सार्वजनिक सर्वर" SUMMARY_SPEECH_PUBLIC_HARDWARE="सार्वजनिक सर्वर (लोकल: 8 GB वाला Pi 5 या बेहतर)" -SUMMARY_SPEECH_PUBLIC_SETUP="सार्वजनिक सर्वर (लोकल: alpha और virtualenv)" +SUMMARY_SPEECH_PUBLIC_SETUP="सार्वजनिक सर्वर (यहाँ लोकल उपलब्ध नहीं)" SUMMARY_SPEECH_UNUSED="यह प्रोफ़ाइल इसका उपयोग नहीं करती" export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/it-it/summary.sh b/tui/locales/it-it/summary.sh index 24fefa5a..31ef4b60 100644 --- a/tui/locales/it-it/summary.sh +++ b/tui/locales/it-it/summary.sh @@ -26,7 +26,7 @@ SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be sk SUMMARY_SPEECH_LOCAL="su questa macchina" SUMMARY_SPEECH_PUBLIC="server pubblici" SUMMARY_SPEECH_PUBLIC_HARDWARE="server pubblici (locale: Pi 5 con 8 GB o superiore)" -SUMMARY_SPEECH_PUBLIC_SETUP="server pubblici (locale: alpha e virtualenv)" +SUMMARY_SPEECH_PUBLIC_SETUP="server pubblici (locale non disponibile qui)" SUMMARY_SPEECH_UNUSED="non usata da questo profilo" export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/kab-dz/summary.sh b/tui/locales/kab-dz/summary.sh index a7a08b64..c5390e3a 100644 --- a/tui/locales/kab-dz/summary.sh +++ b/tui/locales/kab-dz/summary.sh @@ -26,7 +26,7 @@ SUMMARY_STATE_MISSING_CONFIGURATION="yettwafran (txuṣṣ tawila; as yettwazgal SUMMARY_SPEECH_LOCAL="ɣef tmacint-a" SUMMARY_SPEECH_PUBLIC="iqeddacen izayazen" SUMMARY_SPEECH_PUBLIC_HARDWARE="iqeddacen izayazen (adigan: Pi 5 s 8 GB neɣ ugar)" -SUMMARY_SPEECH_PUBLIC_SETUP="iqeddacen izayazen (adigan: alpha d virtualenv)" +SUMMARY_SPEECH_PUBLIC_SETUP="iqeddacen izayazen (adigan ulac-it dagi)" SUMMARY_SPEECH_UNUSED="ur yettwaseqdac ara deg umaɣnu-a" export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/nl-nl/summary.sh b/tui/locales/nl-nl/summary.sh index 0b436bf5..d3515e2b 100644 --- a/tui/locales/nl-nl/summary.sh +++ b/tui/locales/nl-nl/summary.sh @@ -26,7 +26,7 @@ SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be sk SUMMARY_SPEECH_LOCAL="op deze machine" SUMMARY_SPEECH_PUBLIC="openbare servers" SUMMARY_SPEECH_PUBLIC_HARDWARE="openbare servers (lokaal: Pi 5 met 8 GB of beter)" -SUMMARY_SPEECH_PUBLIC_SETUP="openbare servers (lokaal: alpha en virtualenv)" +SUMMARY_SPEECH_PUBLIC_SETUP="openbare servers (lokaal hier niet beschikbaar)" SUMMARY_SPEECH_UNUSED="niet gebruikt door dit profiel" export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/pl-pl/summary.sh b/tui/locales/pl-pl/summary.sh index 724bb275..6685fbad 100644 --- a/tui/locales/pl-pl/summary.sh +++ b/tui/locales/pl-pl/summary.sh @@ -26,7 +26,7 @@ SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be sk SUMMARY_SPEECH_LOCAL="na tym komputerze" SUMMARY_SPEECH_PUBLIC="serwery publiczne" SUMMARY_SPEECH_PUBLIC_HARDWARE="serwery publiczne (lokalnie: Pi 5 z 8 GB lub lepszy)" -SUMMARY_SPEECH_PUBLIC_SETUP="serwery publiczne (lokalnie: alpha i virtualenv)" +SUMMARY_SPEECH_PUBLIC_SETUP="serwery publiczne (tryb lokalny tu niedostępny)" SUMMARY_SPEECH_UNUSED="nieużywana w tym profilu" export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/tui/locales/pt-pt/summary.sh b/tui/locales/pt-pt/summary.sh index ef9b154f..990df843 100644 --- a/tui/locales/pt-pt/summary.sh +++ b/tui/locales/pt-pt/summary.sh @@ -26,7 +26,7 @@ SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be sk SUMMARY_SPEECH_LOCAL="nesta máquina" SUMMARY_SPEECH_PUBLIC="servidores públicos" SUMMARY_SPEECH_PUBLIC_HARDWARE="servidores públicos (local: Pi 5 com 8 GB ou melhor)" -SUMMARY_SPEECH_PUBLIC_SETUP="servidores públicos (local: alpha e virtualenv)" +SUMMARY_SPEECH_PUBLIC_SETUP="servidores públicos (local indisponível aqui)" SUMMARY_SPEECH_UNUSED="não utilizado por este perfil" export CONTENT TITLE SUMMARY_STATE_ENABLED SUMMARY_STATE_DISABLED SUMMARY_STATE_UNSUPPORTED_PROFILE SUMMARY_STATE_MISSING_URL SUMMARY_STATE_MISSING_CONFIGURATION SUMMARY_SPEECH_LOCAL SUMMARY_SPEECH_PUBLIC SUMMARY_SPEECH_PUBLIC_HARDWARE SUMMARY_SPEECH_PUBLIC_SETUP SUMMARY_SPEECH_UNUSED diff --git a/utils/speech.sh b/utils/speech.sh index 503933ea..e8f882d5 100644 --- a/utils/speech.sh +++ b/utils/speech.sh @@ -71,13 +71,22 @@ function detect_local_speech_support() { } # Capable hardware is not enough. Local speech is only offered on the alpha -# channel, the container images do not carry its plugins, and a server has no -# audio to process. +# channel, and a server has no audio to process. A containers satellite runs the +# hivemind-docker images, which do not carry the speech plugins. function local_speech_available() { - [ "${LOCAL_SPEECH_CAPABLE:-false}" == "true" ] && - [ "${CHANNEL:-}" == "alpha" ] && - [ "${METHOD:-}" == "virtualenv" ] && + [ "${LOCAL_SPEECH_CAPABLE:-false}" == "true" ] && [ "${CHANNEL:-}" == "alpha" ] || return 1 + + case "${METHOD:-}" in + virtualenv) [ "${PROFILE:-}" != "server" ] + ;; + containers) + [ "${PROFILE:-}" == "ovos" ] || [ "${PROFILE:-}" == "listener" ] + ;; + *) + return 1 + ;; + esac } # Settle SPEECH_ENGINE before the playbook runs. A scenario file, an earlier @@ -85,7 +94,7 @@ function local_speech_available() { # all ask for local where it is not available; those runs get public. function normalize_speech_engine() { if [ "${SPEECH_ENGINE:-}" == "local" ] && ! local_speech_available; then - echo "Local speech needs the alpha channel, a virtualenv install, a profile with audio and a Raspberry Pi 5 with 8 GB or an equivalent machine. Using the public servers instead." | tee -a "$LOG_FILE" + echo "Local speech needs the alpha channel, a profile with audio (ovos or listener with containers) and a Raspberry Pi 5 with 8 GB or an equivalent machine. Using the public servers instead." | tee -a "$LOG_FILE" fi if [ "${SPEECH_ENGINE:-}" == "local" ] && local_speech_available; then From 7f59b2c3237d197c7d28448c6a344559d82ce3f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Tue, 6 Oct 2026 21:02:59 -0400 Subject: [PATCH 06/14] Exercise local speech in containers every night With ovos-docker pinned to the commit that gives the listener and audio services their model volumes and memory limits (#649), containers installs can run speech on the device. The nightly matrix gains two alpha containers runs, on x86-64 and arm, that install it from a scenario file. The voice speaks in ovos_audio and the listener hears it in ovos_listener, through the /tmp/mycroft folder both containers mount, and the uninstall has to take the ovos_stt_models and ovos_tts_models volumes away. Co-Authored-By: Claude Opus 5.5 --- .github/scripts/assert_local_speech.sh | 71 ++++++++++++++-------- .github/workflows/scenarios-ubuntu2404.yml | 32 ++++++++-- tests/bats/code_quality.bats | 7 +++ 3 files changed, 82 insertions(+), 28 deletions(-) diff --git a/.github/scripts/assert_local_speech.sh b/.github/scripts/assert_local_speech.sh index a31d177c..51b50a75 100755 --- a/.github/scripts/assert_local_speech.sh +++ b/.github/scripts/assert_local_speech.sh @@ -7,51 +7,74 @@ # pair has to work: the configured voice says a sentence and the configured # recognizer hears it back. Nothing goes to the public servers on the way. # +# assert_local_speech.sh [venv-python] a virtualenv install +# assert_local_speech.sh --containers a containers install: the voice speaks in +# ovos_audio, the listener hears it in +# ovos_listener, through the /tmp/mycroft +# folder both of them mount +# # Never run this under sudo. HOME decides which mycroft.conf is read and where the # models were downloaded, and both belong to the install user. set -euo pipefail -python_bin="${1:-${HOME}/.venvs/ovos/bin/python}" +speak=$( + cat <<'PY' +import sys +from ovos_config import Configuration +from ovos_plugin_manager.tts import load_tts_plugin -if [ ! -x "${python_bin}" ]; then - echo "no virtualenv python at ${python_bin}" >&2 - exit 1 -fi +config = Configuration() +tts, lang = config["tts"], config["lang"] +if tts.get("module") != "ovos-tts-plugin-phoonnx": + raise SystemExit(f"TTS is {tts.get('module')!r}, not the local phoonnx plugin") +voice = dict(tts.get(tts["module"]) or {}, lang=lang) +load_tts_plugin(tts["module"])(config=voice).get_tts("what time is it", sys.argv[1]) +print(f"voice: {voice.get('voice')}") +PY +) -"${python_bin}" - <<'PY' +hear=$( + cat <<'PY' import os -import tempfile - +import sys from ovos_config import Configuration from ovos_plugin_manager.stt import load_stt_plugin -from ovos_plugin_manager.tts import load_tts_plugin from ovos_plugin_manager.utils.audio import AudioData config = Configuration() -stt, tts, lang = config["stt"], config["tts"], config["lang"] - +stt, lang = config["stt"], config["lang"] if stt.get("module") != "ovos-stt-plugin-onnx-asr": raise SystemExit(f"STT is {stt.get('module')!r}, not the local onnx-asr plugin") if stt.get("fallback_module") != "ovos-stt-plugin-server": raise SystemExit(f"STT fallback is {stt.get('fallback_module')!r}, not the public server") -if tts.get("module") != "ovos-tts-plugin-phoonnx": - raise SystemExit(f"TTS is {tts.get('module')!r}, not the local phoonnx plugin") models = os.path.expanduser("~/.local/share/ovos_stt_plugin_onnxasr") if not os.path.isdir(models) or not os.listdir(models): raise SystemExit(f"no STT model in {models}: the install did not fetch it") -stt_config = dict(stt.get(stt["module"]) or {}, lang=lang) -tts_config = dict(tts.get(tts["module"]) or {}, lang=lang) - -wav = os.path.join(tempfile.mkdtemp(), "local-speech.wav") -load_tts_plugin(tts["module"])(config=tts_config).get_tts("what time is it", wav) -heard = load_stt_plugin(stt["module"])(config=stt_config).execute( - AudioData.from_file(wav), language=lang) or "" - +recognizer = dict(stt.get(stt["module"]) or {}, lang=lang) +heard = load_stt_plugin(stt["module"])(config=recognizer).execute( + AudioData.from_file(sys.argv[1]), language=lang) or "" if "time" not in heard.lower(): raise SystemExit(f"the voice said 'what time is it' and the recognizer heard {heard!r}") - -print(f"local speech ok: {stt_config.get('model')} heard {heard!r} " - f"from {tts_config.get('voice')}") +print(f"local speech ok: {recognizer.get('model')} heard {heard!r}") PY +) + +if [ "${1:-}" = "--containers" ]; then + wav=/tmp/mycroft/local-speech.wav + docker exec ovos_audio python3 -c "$speak" "$wav" + docker exec ovos_listener python3 -c "$hear" "$wav" + exit 0 +fi + +python_bin="${1:-${HOME}/.venvs/ovos/bin/python}" +if [ ! -x "${python_bin}" ]; then + echo "no virtualenv python at ${python_bin}" >&2 + exit 1 +fi + +wav="$(mktemp --suffix=.wav)" +trap 'rm -f "$wav"' EXIT +"${python_bin}" -c "$speak" "$wav" +"${python_bin}" -c "$hear" "$wav" diff --git a/.github/workflows/scenarios-ubuntu2404.yml b/.github/workflows/scenarios-ubuntu2404.yml index 75aad84a..db0983e7 100644 --- a/.github/workflows/scenarios-ubuntu2404.yml +++ b/.github/workflows/scenarios-ubuntu2404.yml @@ -228,9 +228,9 @@ jobs: - all speech_engine: - public - # Local speech is alpha and virtualenv only. Both runners have the 16 GB and - # the AVX2/NEON the installer's hardware check asks for, so a run that falls - # back to public here is a regression, not a runner limit. + # Local speech is alpha only. Both runners have the 16 GB and the AVX2/NEON the + # installer's hardware check asks for, so a run that falls back to public here + # is a regression, not a runner limit. include: - runner: ubuntu-24.04 method: virtualenv @@ -242,6 +242,16 @@ jobs: channel: alpha feature_set: all speech_engine: local + - runner: ubuntu-24.04 + method: containers + channel: alpha + feature_set: all + speech_engine: local + - runner: ubuntu-24.04-arm + method: containers + channel: alpha + feature_set: all + speech_engine: local runs-on: ${{ matrix.runner }} timeout-minutes: 120 @@ -401,7 +411,12 @@ jobs: - name: Validate speech runs on the device if: matrix.speech_engine == 'local' - run: .github/scripts/assert_local_speech.sh "$HOME/.venvs/ovos/bin/python" + run: | + if [ "${{ matrix.method }}" = "containers" ]; then + .github/scripts/assert_local_speech.sh --containers + else + .github/scripts/assert_local_speech.sh "$HOME/.venvs/ovos/bin/python" + fi - name: Create uninstall scenario file shell: bash @@ -427,6 +442,15 @@ jobs: - name: Validate the uninstall removed the speech models if: matrix.speech_engine == 'local' run: | + if [ "${{ matrix.method }}" = "containers" ]; then + for volume in ovos_stt_models ovos_tts_models; do + if docker volume inspect "$volume" >/dev/null 2>&1; then + echo "the uninstall left the ${volume} volume behind" >&2 + exit 1 + fi + done + exit 0 + fi for path in "$HOME/.local/share/ovos_stt_plugin_onnxasr" "$HOME/.cache/phoonnx"; do if [ -e "$path" ]; then echo "the uninstall left ${path} behind" >&2 diff --git a/tests/bats/code_quality.bats b/tests/bats/code_quality.bats index 87ed4d77..8135b93f 100644 --- a/tests/bats/code_quality.bats +++ b/tests/bats/code_quality.bats @@ -5191,6 +5191,13 @@ for template in ('core-requirements.txt.j2', 'satellite-requirements.txt.j2'): run grep -qF 'the uninstall left ${path} behind' "$workflow" assert_success [ -x .github/scripts/assert_local_speech.sh ] + + # Containers too: the voice speaks in ovos_audio, the listener hears it in + # ovos_listener, and the uninstall takes the model volumes away. + run grep -qF '.github/scripts/assert_local_speech.sh --containers' "$workflow" + assert_success + run grep -qF 'the uninstall left the ${volume} volume behind' "$workflow" + assert_success } @test "local_speech_reaches_containers_only_when_ovos_docker_can_run_it" { From 8ad899dac5a00e4fb7b6092e72b92a2550f7230f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Tue, 6 Oct 2026 23:41:51 -0400 Subject: [PATCH 07/14] Keep a half that fails here on the public servers, and restart onto local speech The first end-to-end runs, and a re-read of what the tasks do, found four ways a local speech install could end up silent, deaf or stuck on the public servers. autoconfigure ran with --offline for local speech. That wrote ovos-config's offline recommendation into both halves, and speech.yml then replaced only the halves it proved work. A half that failed kept the recommendation that had just failed it. In Spanish that is a voice phoonnx 1.3.4a1 cannot speak, so a containers install was mute. autoconfigure now always runs --online: public is the baseline, and each half proven to work replaces it. A re-run kept a local section the last run wrote even when that half no longer worked. A half with nothing working now keeps a public server section (the locale's, from autoconfigure) and loses any other, so OVOS falls back to its public defaults. Running services never saw the result. ovos_utils' watcher reloads a file rewritten in place, and the copy module replaces it. In containers the listener and audio service were already up on autoconfigure's configuration, and in the virtualenv a re-run that switched to local only restarted the units when the template changed. Containers now restart those two services, and the units restart, whenever the speech write changed mycroft.conf. speech_setup.py could also stop the install. ovos_utils logs to stdout, which the task parses as JSON, and a crash, or a download that stalled without failing, ended or hung the play over an optional feature. Its stdout now carries the result alone; a fault costs one half, with the reason reported; each candidate gets 30 minutes; and the task falls back to public rather than failing. The local STT section also keeps the public servers ovos-config picks for the locale (Spanish has its own), so the fallback uses them instead of the plugin's defaults. The five new speech.bats cases run the real speech.yml under ansible-playbook against a stub interpreter. Co-Authored-By: Claude Opus 5.5 --- .../roles/ovos_config/files/speech_setup.py | 68 +++++++-- ansible/roles/ovos_config/tasks/speech.yml | 64 +++++++- .../roles/ovos_containers/tasks/composer.yml | 37 ++++- ansible/roles/ovos_services/tasks/systemd.yml | 10 +- ansible/roles/ovos_virtualenv/tasks/venv.yml | 8 +- scripts/test_speech_setup.py | 66 ++++++++- tests/bats/code_quality.bats | 31 +++- tests/bats/speech.bats | 137 +++++++++++++++++- 8 files changed, 383 insertions(+), 38 deletions(-) diff --git a/ansible/roles/ovos_config/files/speech_setup.py b/ansible/roles/ovos_config/files/speech_setup.py index a2a7c3eb..8e8ad7a0 100644 --- a/ansible/roles/ovos_config/files/speech_setup.py +++ b/ansible/roles/ovos_config/files/speech_setup.py @@ -12,7 +12,13 @@ kept choice is ready for the first utterance. Prints {"stt": section or null, "tts": section or null, "notes": [...]}. null -means nothing worked here, and that half stays on the public servers. +means nothing worked here, and that half stays on the public servers. A kept STT +section also carries the public servers ovos-config recommends for the locale, +for the fallback the installer adds to it. + +The installer parses what this prints as JSON, and ovos_utils logs to stdout, so +stdout is pointed at stderr for the whole run and the result alone goes to the +real one. """ import json import os @@ -21,12 +27,13 @@ import subprocess import sys -import ovos_config -from ovos_config.config import LocalConf +# Each candidate gets this long to download its model, load it and run once. That +# is far more than any of them needs - it is there so that a download that stalls +# without failing cannot hold the whole installation forever. +CANDIDATE_TIMEOUT = int(os.environ.get("OVOS_SPEECH_CANDIDATE_TIMEOUT", "1800")) kinds, lang, gender = sys.argv[1].split(","), sys.argv[2], sys.argv[3] budget_mb = int(sys.argv[4]) if len(sys.argv) > 4 else 3072 -recommends = os.path.join(os.path.dirname(ovos_config.__file__), "recommends") notes = [] # Each candidate is tried in a process of its own: a rejected 5 GB model then @@ -60,7 +67,9 @@ def find(folder): - path = os.path.join(recommends, folder) + import ovos_config + + path = os.path.join(os.path.dirname(ovos_config.__file__), "recommends", folder) if not os.path.isdir(path): return None try: @@ -79,6 +88,8 @@ def find(folder): def recommended(folder, key): + from ovos_config.config import LocalConf + path = find(folder) return LocalConf(path).get(key) if path else None @@ -162,8 +173,13 @@ def first_working(kind, candidates): if model in tried: continue tried.add(model) - run = subprocess.run([sys.executable, "-c", TRY, kind, lang, json.dumps(section)], - capture_output=True, text=True) + try: + run = subprocess.run([sys.executable, "-c", TRY, kind, lang, json.dumps(section)], + capture_output=True, text=True, timeout=CANDIDATE_TIMEOUT) + except subprocess.TimeoutExpired: + notes.append(f"{kind.upper()} {model} ({origin}) was given up on after " + f"{CANDIDATE_TIMEOUT} seconds") + continue lines = [line for line in run.stdout.splitlines() if line.startswith("{")] if run.returncode != 0 or not lines: reason = (run.stderr.strip().splitlines() or ["it did not start"])[-1] @@ -180,10 +196,42 @@ def first_working(kind, candidates): return None +def with_public_fallback(stt): + """Keep the locale's public STT servers in the section, for its fallback. + + The section replaces the whole of mycroft.conf's stt, and with it the servers + `ovos-config autoconfigure --online` picked for the language - Spanish gets one + with a Spanish model. Without them the fallback would use the plugin's default + servers instead. + """ + online = recommended("online_stt", "stt") or {} + module = online.get("module") + if stt is None or not module or not online.get(module): + return stt + return dict(stt, **{module: online[module]}) + + +def set_up(kind, candidates): + """first_working(), except that a fault in this script costs the half, not the run.""" + try: + return first_working(kind, candidates()) + except Exception as error: + notes.append(f"{kind.upper()} could not be set up: {type(error).__name__}: {error}") + return None + + +result_stream = os.fdopen(os.dup(1), "w") +os.dup2(2, 1) + result = {"stt": None, "tts": None} if "stt" in kinds: - result["stt"] = first_working("stt", stt_candidates()) + result["stt"] = set_up("stt", stt_candidates) + try: + result["stt"] = with_public_fallback(result["stt"]) + except Exception as error: + notes.append(f"STT fallback keeps the default public servers: {error}") if "tts" in kinds: - result["tts"] = first_working("tts", tts_candidates()) + result["tts"] = set_up("tts", tts_candidates) result["notes"] = notes -print(json.dumps(result)) +result_stream.write(json.dumps(result) + "\n") +result_stream.flush() diff --git a/ansible/roles/ovos_config/tasks/speech.yml b/ansible/roles/ovos_config/tasks/speech.yml index d825589f..bd5c6016 100644 --- a/ansible/roles/ovos_config/tasks/speech.yml +++ b/ansible/roles/ovos_config/tasks/speech.yml @@ -8,9 +8,18 @@ # per-language defaults for units, wake words and intents, which this installer # sets itself. # +# A half that does not work here stays on the public servers: whatever the caller +# had there before this ran - the public defaults, or the per-language public +# servers `ovos-config autoconfigure --online` wrote - is left alone, and a local +# section an earlier run wrote is removed rather than kept failing. A setup that +# cannot run at all ends the same way, with the reason printed, rather than ending +# the installation over an optional feature. +# # The caller says where OVOS runs: ovos_config_speech_python, the virtualenv's # Python, or ovos_config_speech_containers, the names of the listener and audio -# containers. +# containers. The write is registered as ovos_config_speech_configuration: the +# services only notice a mycroft.conf that is rewritten in place, and this one is +# replaced, so the caller restarts them when it changed. - name: Pick, download and check the local speech plugins become: true become_user: "{{ ovos_installer_user }}" @@ -25,6 +34,7 @@ - "{{ ovos_config_speech_stt_memory_budget_mb }}" register: ovos_config_speech_setup changed_when: false + failed_when: false when: ovos_config_speech_containers is not defined # Each half is set up where it will run - the recognizer in the listener, the @@ -50,6 +60,7 @@ label: "{{ item.kind }}" register: ovos_config_speech_setup_containers changed_when: false + failed_when: false # The listener and audio containers only exist in the desktop profiles. when: - ovos_config_speech_containers is defined @@ -59,12 +70,27 @@ # server for that utterance instead of leaving the user unanswered. - name: Keep what works on this machine vars: - _results: >- + _runs: >- {{ - (ovos_config_speech_setup_containers.results | map(attribute='stdout') | map('from_json') | list) + ovos_config_speech_setup_containers.results if ovos_config_speech_containers is defined - else [ovos_config_speech_setup.stdout | from_json] + else [ovos_config_speech_setup] }} + # The result is the last line the script prints. A run that failed, or printed + # no result, keeps nothing and says why. + _results: >- + {%- set results = [] -%} + {%- for run in _runs -%} + {%- set last = ((run.stdout | default('', true) | trim).split('\n') | last) -%} + {%- if (run.rc | default(1)) == 0 and last.startswith('{') -%} + {%- set _ = results.append(last | from_json) -%} + {%- else -%} + {%- set reason = (run.stderr | default(run.msg | default(''), true) | trim).split('\n') | last -%} + {%- set _ = results.append({'stt': none, 'tts': none, 'notes': [ + 'The local speech setup did not finish: ' ~ (reason | default('no output', true))[:300]]}) -%} + {%- endif -%} + {%- endfor -%} + {{ results }} _stt: "{{ _results | map(attribute='stt') | select | list | first | default(none) }}" _tts: "{{ _results | map(attribute='tts') | select | list | first | default(none) }}" ansible.builtin.set_fact: @@ -88,13 +114,14 @@ machine, so {{ item | upper }} stays on the public servers. loop: "{{ ['stt', 'tts'] | difference(ovos_config_speech_wanted.keys() | list) }}" +# Read even when nothing works here: a local section an earlier run wrote may +# have to go. - name: Read mycroft.conf for the speech configuration ansible.builtin.slurp: src: "{{ ovos_config_mycroft_conf_path }}" register: ovos_config_speech_conf failed_when: false changed_when: false - when: ovos_config_speech_wanted | length > 0 # Same reasoning as homescreen.yml: a hand-edited mycroft.conf that is not # plain JSON is not a reason to end the install. @@ -119,19 +146,42 @@ was not configured and the public servers stay in use. To switch, add these sections to it by hand: {{ ovos_config_speech_wanted | to_json }} +# A half with nothing that works here keeps a public server section, and loses +# any other: in the virtualenv that leaves OVOS's own defaults, the public +# servers, and in containers autoconfigure --online has just written the public +# section for the locale, which stays. - name: Write the local speech configuration vars: _conf: "{{ ovos_config_speech_conf_current | default({}, true) }}" + _public_modules: + - ovos-stt-plugin-server + - ovos-tts-plugin-server + _stale: >- + {{ + ['stt', 'tts'] + | reject('in', ovos_config_speech_wanted) + | select('in', _conf) + | reject('in', _conf | dict2items + | selectattr('value', 'mapping') + | selectattr('value.module', 'defined') + | selectattr('value.module', 'in', _public_modules) + | map(attribute='key') | list) + | list + }} + _final: >- + {{ _conf | dict2items | rejectattr('key', 'in', _stale) | items2dict + | combine(ovos_config_speech_wanted) }} ansible.builtin.copy: - content: "{{ _conf | combine(ovos_config_speech_wanted) | to_nice_json }}\n" + content: "{{ _final | to_nice_json }}\n" dest: "{{ ovos_config_mycroft_conf_path }}" owner: "{{ ovos_config_mycroft_conf_owner }}" group: "{{ ovos_config_mycroft_conf_group }}" mode: "{{ ovos_config_mycroft_conf_mode }}" + register: ovos_config_speech_configuration when: - ovos_config_speech_conf_readable | default(false) | bool - _conf is mapping - - (_conf | combine(ovos_config_speech_wanted)) != _conf + - _final != _conf - name: Record where speech runs vars: diff --git a/ansible/roles/ovos_containers/tasks/composer.yml b/ansible/roles/ovos_containers/tasks/composer.yml index cd48ac8b..c82b93ee 100644 --- a/ansible/roles/ovos_containers/tasks/composer.yml +++ b/ansible/roles/ovos_containers/tasks/composer.yml @@ -131,17 +131,20 @@ - (ovos_installer_speech_engine | default('public')) == 'local' - ovos_containers_speech_engine == 'public' -# --offline or --online, never neither: without one autoconfigure picks its hybrid -# mode (voice on the device, recognition on the public servers), which is neither -# of the two choices the installer offers. +# Always --online, for local speech too. The public servers are the baseline for +# both halves, and local speech (below) replaces each half it proves works here. +# --offline would leave a half that fails that proof on the offline +# recommendation that just failed it - a Spanish voice the installed phoonnx +# cannot speak, which leaves the device mute. And never neither: without a mode +# autoconfigure picks its hybrid one (voice on the device, recognition on the +# public servers), which is not one of the two choices the installer offers. - name: Merge ovos-config language recommendations into mounted config become: true become_user: "{{ ovos_installer_user }}" community.docker.docker_container_exec: container: "{{ ovos_containers_container_ovos_cli }}" command: >- - ovos-config autoconfigure --lang {{ ovos_installer_locale }} - {{ '--offline' if ovos_containers_speech_engine == 'local' else '--online' }} + ovos-config autoconfigure --lang {{ ovos_installer_locale }} --online {{ ovos_installer_ovos_config_tts_gender }} when: - not ansible_check_mode @@ -166,6 +169,30 @@ - ovos_containers_speech_engine == 'local' - ovos_containers_is_desktop_profile | bool +# The listener and the audio service are already running on what autoconfigure +# wrote, and they only reload a mycroft.conf rewritten in place. Local speech +# replaces the file, which their watcher never sees, so without a restart they +# would stay on the public servers until the next boot. +- name: Restart the listener and audio services on their local speech + become: true + become_user: "{{ ovos_installer_user }}" + community.docker.docker_compose_v2: + project_src: "{{ ovos_containers_composition_directory }}" + project_name: "{{ ovos_containers_compose_project_name }}" + files: "{{ ovos_containers_composition_files }}" + services: + - "{{ ovos_containers_container_ovos_listener }}" + - "{{ ovos_containers_container_ovos_audio }}" + state: restarted + environment: + DOCKER_TIMEOUT: "{{ ovos_containers_docker_timeout }}" + when: + - not ansible_check_mode + - ovos_containers_speech_engine == 'local' + - ovos_containers_is_desktop_profile | bool + - ovos_config_speech_configuration is defined + - ovos_config_speech_configuration is changed + - name: Compute optional skills flags vars: _ovos_homeassistant_url: >- diff --git a/ansible/roles/ovos_services/tasks/systemd.yml b/ansible/roles/ovos_services/tasks/systemd.yml index c240f424..4af18b8b 100644 --- a/ansible/roles/ovos_services/tasks/systemd.yml +++ b/ansible/roles/ovos_services/tasks/systemd.yml @@ -82,8 +82,16 @@ - name: Enable and start OVOS and/or HiveMind systemd units vars: + # Local speech rewrites mycroft.conf after the template, and by replacing it, + # which running services do not notice: a run that switches to it, or changes + # what it picked, restarts them too. ovos_installer_systemd_state: >- - {{ 'restarted' if (ovos_config_configuration.changed | default(false)) | bool else 'started' }} + {{ + 'restarted' + if (ovos_config_configuration.changed | default(false)) | bool + or (ovos_config_speech_configuration.changed | default(false)) | bool + else 'started' + }} ovos_installer_ovos_scope: "{{ ovos_installer_systemd_scope | default('user') }}" ovos_installer_ovos_unit_user: "{{ 'root' if ovos_installer_ovos_scope == 'system' else ovos_installer_user }}" ovos_services_unit_user: "{{ 'root' if item.scope == 'system' else ovos_installer_ovos_unit_user }}" diff --git a/ansible/roles/ovos_virtualenv/tasks/venv.yml b/ansible/roles/ovos_virtualenv/tasks/venv.yml index e031a00f..dfd7edfc 100644 --- a/ansible/roles/ovos_virtualenv/tasks/venv.yml +++ b/ansible/roles/ovos_virtualenv/tasks/venv.yml @@ -516,9 +516,11 @@ - autoconfigure - --lang - "{{ ovos_installer_locale }}" - # Never neither: without a mode autoconfigure picks its hybrid one, which is - # not one of the two choices the installer offers. - - "{{ '--offline' if (ovos_installer_speech_engine | default('public')) == 'local' else '--online' }}" + # Always --online: local speech replaces each half it proves works here + # afterwards, and a half that does not stays public instead of on an offline + # recommendation that just failed. Never neither: without a mode autoconfigure + # picks its hybrid one, which is not one of the two choices the installer offers. + - --online - "{{ ovos_installer_ovos_config_tts_gender }}" environment: PATH: "{{ ovos_virtualenv_path }}/bin" diff --git a/scripts/test_speech_setup.py b/scripts/test_speech_setup.py index 4dc6161f..095227c5 100644 --- a/scripts/test_speech_setup.py +++ b/scripts/test_speech_setup.py @@ -22,8 +22,15 @@ TTS = "ovos-tts-plugin-phoonnx" STUBS = { + # ovos_utils logs to stdout; STUB_NOISY makes the stand-in do the same, at the + # Python and at the file descriptor level, the way an import really can. "ovos_config/config.py": """ - import json + import json, os, sys + + if os.environ.get("STUB_NOISY"): + print("2026-10-07 - OVOS - ovos_config:load:1 - INFO - loading configuration") + sys.stdout.flush() + os.write(1, b"raw write from an extension\\n") class LocalConf(dict): @@ -38,13 +45,19 @@ def __init__(self, frames, rate, width): self.frames = frames """, # A model whose name starts with "broken" fails to load, one starting with "huge" - # holds 200 MB once loaded; anything else loads and hears nothing. + # holds 200 MB once loaded, one starting with "stalled" never finishes loading; + # anything else loads and hears nothing. "ovos_plugin_manager/stt.py": """ + import time + + class Recognizer: def __init__(self, config): model = config.get("model", "") if model.startswith("broken"): raise RuntimeError("cannot load " + model) + if model.startswith("stalled"): + time.sleep(60) self.weights = b"x" * (200 * 1024 * 1024) if model.startswith("huge") else b"" def execute(self, audio, language=None): @@ -172,11 +185,13 @@ def find_recommends_file(folder, lang): target.parent.mkdir(parents=True, exist_ok=True) target.write_text(json.dumps(content)) - def setup(self, lang, kinds="stt,tts", gender="male", budget=100, registry=None, voices=None, hub=None): + def setup(self, lang, kinds="stt,tts", gender="male", budget=100, registry=None, voices=None, hub=None, + **env_extra): env = dict(self.env, STUB_STT_REGISTRY=json.dumps(registry or {}), - STUB_VOICES=json.dumps(voices or {}), STUB_HUB=json.dumps(hub or {})) + STUB_VOICES=json.dumps(voices or {}), STUB_HUB=json.dumps(hub or {}), **env_extra) out = subprocess.run([sys.executable, str(SETUP), kinds, lang, gender, str(budget)], capture_output=True, text=True, env=env, check=True).stdout + # The installer parses the whole of stdout, so nothing else may be on it. return json.loads(out) def test_working_recommendations_are_kept_as_they_are(self): @@ -240,6 +255,49 @@ def test_a_repository_without_int8_weights_is_left_alone(self): result = self.setup("pt-pt", kinds="stt", hub={model: ["encoder_model.onnx_data"]}) self.assertNotIn("quantization", result["stt"][STT]) + def test_only_the_result_reaches_stdout(self): + # ovos_utils logs to stdout, and an import that logs or writes would put a line + # in front of the JSON the installer parses - the whole install would stop on it. + self.recommends({"offline_stt/en-us.conf": stt("parakeet"), "offline_male/en-us.conf": tts("miro")}) + result = self.setup("en-us", STUB_NOISY="1") + self.assertIsNotNone(result["stt"]) + self.assertIsNotNone(result["tts"]) + + def test_the_fallback_keeps_the_public_servers_ovos_config_picks_for_the_locale(self): + # The local section replaces the whole stt section, and with it the servers + # autoconfigure --online chose for the language; the fallback needs them back. + servers = {"urls": ["https://zuazo-es.tigregotico.pt/stt", "https://stt.smartgic.io/fasterwhisper/stt"]} + self.recommends({"offline_stt/es-es.conf": stt("parakeet-es"), + "online_stt/es-es.conf": {"stt": {"module": "ovos-stt-plugin-server", + "ovos-stt-plugin-server": servers}}}) + result = self.setup("es-es", kinds="stt") + self.assertEqual(result["stt"][STT]["model"], "parakeet-es") + self.assertEqual(result["stt"]["ovos-stt-plugin-server"], servers) + + def test_without_public_servers_for_the_locale_the_fallback_keeps_its_defaults(self): + self.recommends({"offline_stt/kab-dz.conf": stt("kab-conformer")}) + result = self.setup("kab-dz", kinds="stt") + self.assertNotIn("ovos-stt-plugin-server", result["stt"]) + + def test_a_fault_while_choosing_costs_that_half_and_not_the_run(self): + # A recommendation that is not even JSON: the script must still answer, with the + # other half set up and the reason in the notes, rather than exit without a result. + self.recommends({"offline_male/en-us.conf": tts("miro")}) + broken = self.root / "stubs" / "ovos_config" / "recommends" / "offline_stt" / "en-us.conf" + broken.parent.mkdir(parents=True, exist_ok=True) + broken.write_text("{ not json") + result = self.setup("en-us") + self.assertIsNone(result["stt"]) + self.assertEqual(result["tts"][TTS]["voice"], "miro") + self.assertTrue(any(note.startswith("STT could not be set up") for note in result["notes"])) + + def test_a_candidate_that_never_finishes_is_given_up_on(self): + self.recommends({"offline_stt/en-us.conf": stt("stalled/parakeet")}) + result = self.setup("en-us", kinds="stt", registry={"en": "OpenVoiceOS/parakeet-en"}, + OVOS_SPEECH_CANDIDATE_TIMEOUT="3") + self.assertEqual(result["stt"][STT]["model"], "OpenVoiceOS/parakeet-en") + self.assertTrue(any("stalled/parakeet" in note and "given up on" in note for note in result["notes"])) + def test_only_the_halves_asked_for_are_set_up(self): self.recommends({"offline_stt/en-us.conf": stt("parakeet"), "offline_male/en-us.conf": tts("miro")}) result = self.setup("en-us", kinds="stt") diff --git a/tests/bats/code_quality.bats b/tests/bats/code_quality.bats index 8135b93f..33e8a063 100644 --- a/tests/bats/code_quality.bats +++ b/tests/bats/code_quality.bats @@ -2699,14 +2699,18 @@ YAML run grep -F -q "{{ ovos_installer_ovos_config_tts_gender }}" "$container_tasks" assert_success - # Always a mode: with neither flag autoconfigure goes hybrid (the voice on the - # device, recognition on the public servers), which is not one of the two - # speech choices the installer offers. - run grep -F -q "{{ '--offline' if ovos_containers_speech_engine == 'local' else '--online' }}" "$container_tasks" + # Always --online. Never neither: autoconfigure then goes hybrid (the voice on the + # device, recognition on the public servers), which is not one of the two speech + # choices the installer offers. And never --offline, not even for local speech: + # local speech replaces each half it proves works afterwards, and --offline would + # leave a half that fails on the offline recommendation that just failed - a + # Spanish voice phoonnx cannot speak, and a mute device. + run grep -F -q "ovos-config autoconfigure --lang {{ ovos_installer_locale }} --online" "$container_tasks" assert_success - - run grep -F -q "{{ '--offline' if (ovos_installer_speech_engine | default('public')) == 'local' else '--online' }}" "$virtualenv_tasks" + run bash -c "awk '/Merge ovos-config language recommendations into mycroft.conf/,/changed_when/' '$virtualenv_tasks' | grep -q -- '- --online$'" assert_success + run bash -c "grep -h -v '^[[:space:]]*#' '$container_tasks' '$virtualenv_tasks' | grep -q -- '--offline'" + assert_failure run grep -F -q "ovos_installer_ovos_config_autoconfigure_enabled | default(false) | bool" "$container_tasks" assert_success @@ -5158,6 +5162,13 @@ for template in ('core-requirements.txt.j2', 'satellite-requirements.txt.j2'): run grep -q "ovos_config_speech_current" ansible/roles/ovos_config/templates/mycroft.conf.j2 assert_success + # Running services only reload a mycroft.conf rewritten in place, and the speech + # write replaces it, so a run that changes it restarts the systemd units too. + run bash -c "grep -A8 'ovos_installer_systemd_state:' ansible/roles/ovos_services/tasks/systemd.yml | grep -q 'ovos_config_speech_configuration.changed'" + assert_success + run grep -q "register: ovos_config_speech_configuration" "$speech_tasks" + assert_success + # Telemetry reports where speech ended up, and uninstall removes the models. local telemetry="ansible/roles/ovos_telemetry/tasks/main.yml" run grep -q 'stt_engine: "{{ ovos_telemetry_stt_engine }}"' "$telemetry" @@ -5222,6 +5233,14 @@ for template in ('core-requirements.txt.j2', 'satellite-requirements.txt.j2'): run grep -q "^ovos_containers_container_ovos_audio: ovos_audio$" ansible/roles/ovos_containers/defaults/main.yml assert_success + # The listener and audio service are up before local speech replaces + # mycroft.conf, and they do not see a replaced file: they are restarted when it + # changed, or they would stay on the public servers until the next boot. + run bash -c "awk '/- name: Restart the listener and audio services on their local speech/,/- name: Compute optional skills flags/' '$composer' | grep -q 'state: restarted'" + assert_success + run bash -c "awk '/- name: Restart the listener and audio services on their local speech/,/- name: Compute optional skills flags/' '$composer' | grep -q 'ovos_config_speech_configuration is changed'" + assert_success + # The raised limits only go into .env when local speech was asked for. local env_template="ansible/roles/ovos_containers/templates/docker/env.j2" run bash -c "grep -B1 '^LISTENER_MEMORY_LIMIT=' '$env_template' | grep -q \"ovos_installer_speech_engine | default('public')) == 'local'\"" diff --git a/tests/bats/speech.bats b/tests/bats/speech.bats index 18a34d55..20dc80b5 100644 --- a/tests/bats/speech.bats +++ b/tests/bats/speech.bats @@ -1,8 +1,9 @@ #!/usr/bin/env bats # # Local speech is offered on a Raspberry Pi 5 with 8 GB or a machine at least as -# capable, on alpha virtualenv installs only. Everything else stays on the -# public servers, whatever was asked for. +# capable, on alpha installs only: the virtualenv, or containers with the ovos or +# listener profile. Everything else stays on the public servers, whatever was +# asked for. function setup() { load "$HOME/shell-testing/test_helper/bats-support/load" @@ -292,3 +293,135 @@ function run_scenario_with_speech_engine() { run local_speech_available assert_failure } + +# The tasks that write local speech into mycroft.conf, run for real against a stand-in +# for the virtualenv's python: it prints a log line, the way ovos_utils does on stdout, +# then the result speech_setup.py would have printed. +function speech_tasks_setup() { + if ! command -v ansible-playbook >/dev/null 2>&1; then + skip "ansible-playbook is not available" + fi + + SPEECH_DIR="$(mktemp -d "${BATS_TEST_TMPDIR:-/tmp}/speech.XXXXXX")" + SPEECH_CONF="$SPEECH_DIR/mycroft.conf" + SPEECH_RESULT="$SPEECH_DIR/result" + SPEECH_RC="$SPEECH_DIR/rc" + printf '0\n' >"$SPEECH_RC" + + cat >"$SPEECH_DIR/python" <&2 +exit "\$rc" +STUB + chmod +x "$SPEECH_DIR/python" + + cat >"$SPEECH_DIR/play.yml" <<'YAML' +- hosts: localhost + gather_facts: false + tasks: + - name: Run the local speech tasks + ansible.builtin.include_role: + name: ovos_config + tasks_from: speech.yml + - name: Say what the run decided + ansible.builtin.debug: + msg: >- + ENGINES={{ ovos_config_speech_stt_engine }},{{ ovos_config_speech_tts_engine }} + CHANGED={{ ovos_config_speech_configuration.changed | default(false) }} +YAML +} + +function run_speech_tasks() { + printf '%s\n' "$1" >"$SPEECH_RESULT" + run ansible-playbook -i localhost, -c local "$SPEECH_DIR/play.yml" -e "{ + \"ansible_become\": false, + \"ovos_config_speech_python\": \"$SPEECH_DIR/python\", + \"ovos_config_mycroft_conf_path\": \"$SPEECH_CONF\", + \"ovos_config_mycroft_conf_owner\": \"$(id -un)\", + \"ovos_config_mycroft_conf_group\": \"$(id -gn)\", + \"ovos_config_mycroft_conf_mode\": \"0644\", + \"ovos_installer_user\": \"$(id -un)\", + \"ovos_installer_locale\": \"es-es\", + \"ovos_installer_ovos_config_tts_gender\": \"--male\" + }" +} + +LOCAL_STT='{"module": "ovos-stt-plugin-onnx-asr", "fallback_module": "", "ovos-stt-plugin-onnx-asr": {"model": "parakeet-es", "quantization": "int8"}, "ovos-stt-plugin-server": {"urls": ["https://zuazo-es.tigregotico.pt/stt"]}}' +LOCAL_TTS='{"module": "ovos-tts-plugin-phoonnx", "ovos-tts-plugin-phoonnx": {"voice": "OpenVoiceOS/pipertts_es-ES_miro"}}' +PUBLIC_TTS='{"module": "ovos-tts-plugin-server", "ovos-tts-plugin-server": {"voice": "sharvard-medium#M"}}' + +@test "speech: what works here is written, with the public STT server as its fallback" { + speech_tasks_setup + printf '{"lang": "es-es", "websocket": {"port": 8181}}\n' >"$SPEECH_CONF" + + run_speech_tasks "{\"stt\": $LOCAL_STT, \"tts\": $LOCAL_TTS, \"notes\": []}" + assert_success + assert_output --partial "ENGINES=local,local CHANGED=True" + + run jq -c '[.stt.module, .stt.fallback_module, .stt["ovos-stt-plugin-server"].urls[0], .tts["ovos-tts-plugin-phoonnx"].voice, .websocket.port]' "$SPEECH_CONF" + assert_output '["ovos-stt-plugin-onnx-asr","ovos-stt-plugin-server","https://zuazo-es.tigregotico.pt/stt","OpenVoiceOS/pipertts_es-ES_miro",8181]' + + # A second run with the same result leaves the file alone, so it restarts nothing. + run_speech_tasks "{\"stt\": $LOCAL_STT, \"tts\": $LOCAL_TTS, \"notes\": []}" + assert_success + assert_output --partial "ENGINES=local,local CHANGED=False" +} + +@test "speech: a half that no longer works here does not keep the local section an earlier run wrote" { + speech_tasks_setup + # The virtualenv: the template carried both of the last run's sections through. + printf '{"lang": "es-es", "stt": %s, "tts": %s}\n' "$LOCAL_STT" "$LOCAL_TTS" >"$SPEECH_CONF" + + run_speech_tasks "{\"stt\": $LOCAL_STT, \"tts\": null, \"notes\": [\"TTS OpenVoiceOS/pipertts_es-ES_miro does not work here\"]}" + assert_success + assert_output --partial "ENGINES=local,public CHANGED=True" + assert_output --partial "No on-device TTS works for es-es" + + # No tts section: OVOS falls back to its own default, the public server. + run jq -c '[.stt.module, has("tts")]' "$SPEECH_CONF" + assert_output '["ovos-stt-plugin-onnx-asr",false]' +} + +@test "speech: a half that does not work here keeps the public section autoconfigure wrote" { + speech_tasks_setup + # Containers: autoconfigure --online has just written the public voice for the locale. + printf '{"lang": "es-es", "tts": %s}\n' "$PUBLIC_TTS" >"$SPEECH_CONF" + + run_speech_tasks "{\"stt\": $LOCAL_STT, \"tts\": null, \"notes\": []}" + assert_success + assert_output --partial "ENGINES=local,public CHANGED=True" + + run jq -c '[.stt.module, .tts.module, .tts["ovos-tts-plugin-server"].voice]' "$SPEECH_CONF" + assert_output '["ovos-stt-plugin-onnx-asr","ovos-tts-plugin-server","sharvard-medium#M"]' +} + +@test "speech: a setup that cannot run leaves speech public instead of ending the install" { + speech_tasks_setup + printf '{"lang": "es-es", "stt": %s, "tts": %s}\n' "$LOCAL_STT" "$PUBLIC_TTS" >"$SPEECH_CONF" + printf '1\n' >"$SPEECH_RC" + + run_speech_tasks "" + assert_success + assert_output --partial "The local speech setup did not finish: ModuleNotFoundError" + assert_output --partial "ENGINES=public,public CHANGED=True" + + run jq -c '[has("stt"), .tts.module]' "$SPEECH_CONF" + assert_output '[false,"ovos-tts-plugin-server"]' +} + +@test "speech: a mycroft.conf that is not plain JSON is left as it is" { + speech_tasks_setup + printf '// hand-edited\n{"lang": "es-es"}\n' >"$SPEECH_CONF" + cp "$SPEECH_CONF" "$SPEECH_DIR/before" + + run_speech_tasks "{\"stt\": $LOCAL_STT, \"tts\": $LOCAL_TTS, \"notes\": []}" + assert_success + assert_output --partial "is not plain JSON" + assert_output --partial "ENGINES=public,public" + + run cmp "$SPEECH_DIR/before" "$SPEECH_CONF" + assert_success +} From 48d39b8aded7917c9568b4e9661b7d23e746454f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Tue, 6 Oct 2026 23:41:51 -0400 Subject: [PATCH 08/14] Ask the running services whether they speak and hear on the device assert_local_speech.sh only checked mycroft.conf and ran the plugins it names in a fresh process. That passes on a device whose services never loaded those plugins, which is exactly what the containers path did until the previous commit. The check now also asks the services themselves: - The running audio service synthesises a sentence over the bus and names the plugin that did it. That must be phoonnx. - The running listener's own log has to show the onnx-asr recognizer loading. - The listener transcribes what the voice said through its main recognizer, never the fallback. A listener only answers the bus once its microphone has started, which no CI runner has. In that case the recognizer hears the audio in a fresh process instead, and the output says which path was taken. assert_local_speech_bus.py reuses assert_intent_roundtrip.py's bare-websocket client, whose wait_for now accepts several reply topics, since the alpha audio service answers on the spec topic. test_local_speech_bus.py drives it against a fake bus, one wrong answer at a time. Co-Authored-By: Claude Opus 5.5 --- .github/scripts/assert_intent_roundtrip.py | 9 +- .github/scripts/assert_local_speech.sh | 135 +++++++++++----- .github/scripts/assert_local_speech_bus.py | 156 ++++++++++++++++++ scripts/test_local_speech_bus.py | 175 +++++++++++++++++++++ tests/bats/code_quality.bats | 7 + 5 files changed, 439 insertions(+), 43 deletions(-) create mode 100755 .github/scripts/assert_local_speech_bus.py create mode 100644 scripts/test_local_speech_bus.py diff --git a/.github/scripts/assert_intent_roundtrip.py b/.github/scripts/assert_intent_roundtrip.py index 99478005..1ab2ddc2 100755 --- a/.github/scripts/assert_intent_roundtrip.py +++ b/.github/scripts/assert_intent_roundtrip.py @@ -68,7 +68,12 @@ def send(self, msg_type, data=None, context=None): "context": context or {"source": ["ci"]}})) def wait_for(self, reply_type, timeout, match=None): - """Read until `reply_type` arrives, or the deadline passes. None on timeout.""" + """Read until `reply_type` arrives, or the deadline passes. None on timeout. + + `reply_type` may be a tuple of types, for an answer that arrives under a spec + topic on newer services and a legacy one on older ones. + """ + reply_types = reply_type if isinstance(reply_type, tuple) else (reply_type,) deadline = time.monotonic() + timeout while True: remaining = deadline - time.monotonic() @@ -85,7 +90,7 @@ def wait_for(self, reply_type, timeout, match=None): message = json.loads(raw) except (TypeError, ValueError): continue - if message.get("type") != reply_type: + if message.get("type") not in reply_types: continue if match and not match(message): continue diff --git a/.github/scripts/assert_local_speech.sh b/.github/scripts/assert_local_speech.sh index 51b50a75..47d374f0 100755 --- a/.github/scripts/assert_local_speech.sh +++ b/.github/scripts/assert_local_speech.sh @@ -1,80 +1,133 @@ #!/usr/bin/env bash # Assert that a local speech install really runs speech on the device. # -# The configuration has to name the onnx-asr and phoonnx plugins, with the public -# server only as the STT fallback; the STT model has to be on disk already, because -# the installer fetches it rather than leaving it to the first utterance; and the -# pair has to work: the configured voice says a sentence and the configured -# recognizer hears it back. Nothing goes to the public servers on the way. +# 1. mycroft.conf names the onnx-asr and phoonnx plugins, with the public server +# only as the STT fallback, and the STT model is on disk already: the installer +# fetches it rather than leaving it to the first utterance; +# 2. the running listener loaded the onnx-asr recognizer - its own log says so; +# 3. the running audio service speaks with phoonnx (assert_local_speech_bus.py +# asks it over the bus, and it names the plugin it used); +# 4. what that voice said, the running listener hears back. A listener with no +# microphone never starts answering the bus, which is every CI runner, and then +# the recognizer hears it in a fresh process instead. +# +# 1 alone was all this used to check, and it passes with services that never loaded +# what the file names - they only reload a mycroft.conf rewritten in place. # # assert_local_speech.sh [venv-python] a virtualenv install -# assert_local_speech.sh --containers a containers install: the voice speaks in -# ovos_audio, the listener hears it in -# ovos_listener, through the /tmp/mycroft -# folder both of them mount +# assert_local_speech.sh --containers a containers install (ovos_listener and +# ovos_audio) # # Never run this under sudo. HOME decides which mycroft.conf is read and where the -# models were downloaded, and both belong to the install user. +# models and logs are, and all of them belong to the install user. set -euo pipefail -speak=$( +here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +stt_config=$( cat <<'PY' -import sys +import os from ovos_config import Configuration -from ovos_plugin_manager.tts import load_tts_plugin -config = Configuration() -tts, lang = config["tts"], config["lang"] +stt = Configuration()["stt"] +if stt.get("module") != "ovos-stt-plugin-onnx-asr": + raise SystemExit(f"STT is {stt.get('module')!r}, not the local onnx-asr plugin") +if stt.get("fallback_module") != "ovos-stt-plugin-server": + raise SystemExit(f"STT fallback is {stt.get('fallback_module')!r}, not the public server") +models = os.path.expanduser("~/.local/share/ovos_stt_plugin_onnxasr") +if not os.path.isdir(models) or not os.listdir(models): + raise SystemExit(f"no STT model in {models}: the install did not fetch it") +print(f"config : STT {(stt.get(stt['module']) or {}).get('model')}, public fallback") +PY +) + +tts_config=$( + cat <<'PY' +from ovos_config import Configuration + +tts = Configuration()["tts"] if tts.get("module") != "ovos-tts-plugin-phoonnx": raise SystemExit(f"TTS is {tts.get('module')!r}, not the local phoonnx plugin") -voice = dict(tts.get(tts["module"]) or {}, lang=lang) -load_tts_plugin(tts["module"])(config=voice).get_tts("what time is it", sys.argv[1]) -print(f"voice: {voice.get('voice')}") +print(f"config : TTS {(tts.get(tts['module']) or {}).get('voice')}") PY ) +# What the voice said, heard in a process of its own. The path is "-" to read the +# audio from stdin, which is how it reaches a container. hear=$( cat <<'PY' -import os import sys +import tempfile from ovos_config import Configuration from ovos_plugin_manager.stt import load_stt_plugin from ovos_plugin_manager.utils.audio import AudioData +path = sys.argv[1] +if path == "-": + with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as wav: + wav.write(sys.stdin.buffer.read()) + path = wav.name config = Configuration() stt, lang = config["stt"], config["lang"] -if stt.get("module") != "ovos-stt-plugin-onnx-asr": - raise SystemExit(f"STT is {stt.get('module')!r}, not the local onnx-asr plugin") -if stt.get("fallback_module") != "ovos-stt-plugin-server": - raise SystemExit(f"STT fallback is {stt.get('fallback_module')!r}, not the public server") - -models = os.path.expanduser("~/.local/share/ovos_stt_plugin_onnxasr") -if not os.path.isdir(models) or not os.listdir(models): - raise SystemExit(f"no STT model in {models}: the install did not fetch it") - recognizer = dict(stt.get(stt["module"]) or {}, lang=lang) heard = load_stt_plugin(stt["module"])(config=recognizer).execute( - AudioData.from_file(sys.argv[1]), language=lang) or "" + AudioData.from_file(path), language=lang) or "" if "time" not in heard.lower(): raise SystemExit(f"the voice said 'what time is it' and the recognizer heard {heard!r}") -print(f"local speech ok: {recognizer.get('model')} heard {heard!r}") +print(f"recognizer: heard {heard!r} in a fresh process") PY ) +wav="$(mktemp --suffix=.wav)" +websocket_dir="" +cleanup() { + rm -f "$wav" + if [ -n "$websocket_dir" ]; then + rm -rf "$websocket_dir" + fi +} +trap cleanup EXIT + if [ "${1:-}" = "--containers" ]; then - wav=/tmp/mycroft/local-speech.wav - docker exec ovos_audio python3 -c "$speak" "$wav" - docker exec ovos_listener python3 -c "$hear" "$wav" - exit 0 + docker exec ovos_listener python3 -c "$stt_config" + docker exec ovos_audio python3 -c "$tts_config" + listener_log() { docker logs ovos_listener 2>&1; } + hear_fresh() { docker exec -i ovos_listener python3 -c "$hear" - <"$wav"; } + # No host venv: the bus check needs websocket-client and nothing else, so it goes + # into a throwaway directory rather than ~/.local (see assert_intent_roundtrip.sh). + bus_python=python3 + if ! python3 -c "import websocket" 2>/dev/null; then + websocket_dir="$(mktemp -d)" + python3 -m pip install --quiet --disable-pip-version-check --no-cache-dir \ + --target "$websocket_dir" websocket-client + export PYTHONPATH="${websocket_dir}${PYTHONPATH:+:${PYTHONPATH}}" + fi +else + venv_python="${1:-${HOME}/.venvs/ovos/bin/python}" + if [ ! -x "${venv_python}" ]; then + echo "no virtualenv python at ${venv_python}" >&2 + exit 1 + fi + "${venv_python}" -c "$stt_config" + "${venv_python}" -c "$tts_config" + listener_log() { cat "${XDG_STATE_HOME:-${HOME}/.local/state}/mycroft/voice.log"; } + hear_fresh() { "${venv_python}" -c "$hear" "$wav"; } + bus_python="${venv_python}" fi -python_bin="${1:-${HOME}/.venvs/ovos/bin/python}" -if [ ! -x "${python_bin}" ]; then - echo "no virtualenv python at ${python_bin}" >&2 +# The plugin logs as it loads its model, and every line names its module. Not grep +# -q: it stops reading at the first match, the log writer dies of SIGPIPE, and +# pipefail turns a found line into a failure. +if ! listener_log | grep -F "ovos_stt_plugin_onnxasr" >/dev/null; then + echo "the running listener never loaded the onnx-asr recognizer" >&2 exit 1 fi +echo "listener : loaded the onnx-asr recognizer" -wav="$(mktemp --suffix=.wav)" -trap 'rm -f "$wav"' EXIT -"${python_bin}" -c "$speak" "$wav" -"${python_bin}" -c "$hear" "$wav" +status=0 +"${bus_python}" "${here}/assert_local_speech_bus.py" --wav "$wav" || status=$? +case "$status" in +0) ;; +3) hear_fresh ;; +*) exit "$status" ;; +esac diff --git a/.github/scripts/assert_local_speech_bus.py b/.github/scripts/assert_local_speech_bus.py new file mode 100755 index 00000000..f90423dd --- /dev/null +++ b/.github/scripts/assert_local_speech_bus.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +"""Have the running services speak and listen, over the messagebus. + +assert_local_speech.sh checks mycroft.conf and the plugins it names. That is not the +same as the services using them: they load their plugins when they start and reload +only a configuration file rewritten in place, so a mycroft.conf they never picked up +passes every check made on the file while the device keeps talking to the public +servers. This asks the services themselves: + + 1. the audio service synthesises a sentence for the bus (speak:b64_audio) and names + the plugin that did it, which must be the local one; + 2. the listener transcribes that audio with its own recognizer + (recognizer_loop:b64_transcribe), never its fallback, and must hear the sentence. + +The listener only starts answering the bus once its microphone has started, and a CI +runner has no microphone. When it never reports ready, the audio is still written to +--wav and this exits 3, so the caller can have the recognizer hear it in a fresh +process instead. + +Like assert_intent_roundtrip.py, which this borrows its bus client from, it speaks the +bus protocol over a bare websocket, so it runs the same on a host with no venv. +""" +import argparse +import base64 +import io +import secrets +import sys +import time +import wave + +from assert_intent_roundtrip import Bus, Failure, wait_ready + +LISTENER_UNAVAILABLE = 3 +LOCAL_TTS = "ovos-tts-plugin-phoonnx" + + +def speak(bus, utterance, timeout): + """The audio service's own TTS, as bytes of a WAV file, and the plugin it used.""" + marker = secrets.token_hex(8) + bus.send("speak:b64_audio", {"utterance": utterance}, + {"source": ["ci"], "ci_probe": marker}) + # Newer services answer on the spec topic, older ones on the legacy one, and the + # bus client may mirror one onto the other: whichever comes first will do. + answer = bus.wait_for(("ovos.audio.speech", "speak:b64_audio.response"), timeout, + match=lambda m: (m.get("context") or {}).get("ci_probe") == marker) + if answer is None: + raise Failure( + f"the audio service did not synthesise {utterance!r} for the bus within " + f"{timeout:.0f}s, having reported itself ready." + ) + data = answer.get("data") or {} + try: + return base64.b64decode(data["audio"]), data.get("tts_id") + except (KeyError, TypeError, ValueError) as error: + raise Failure(f"the audio service answered without usable audio: {error!r}") + + +def pcm(wav_bytes): + """The frames of a mono WAV, with the rate and width the listener needs to read them.""" + try: + with wave.open(io.BytesIO(wav_bytes)) as wav: + if wav.getnchannels() != 1: + raise Failure(f"the voice produced {wav.getnchannels()} channels, not one") + return wav.readframes(wav.getnframes()), wav.getframerate(), wav.getsampwidth() + except (wave.Error, EOFError) as error: + raise Failure(f"the audio service's answer is not a WAV file: {error}") + + +def transcribe(bus, wav_bytes, timeout): + frames, rate, width = pcm(wav_bytes) + marker = secrets.token_hex(8) + bus.send("recognizer_loop:b64_transcribe", + {"audio": base64.b64encode(frames).decode("ascii"), + "sample_rate": rate, "sample_width": width}, + {"source": ["ci"], "ci_probe": marker}) + answer = bus.wait_for("recognizer_loop:b64_transcribe.response", timeout, + match=lambda m: (m.get("context") or {}).get("ci_probe") == marker) + if answer is None: + raise Failure( + f"the listener did not transcribe the audio within {timeout:.0f}s, having " + f"reported itself ready." + ) + heard = [] + for transcription in (answer.get("data") or {}).get("transcriptions") or []: + # (text, confidence) pairs, which JSON turns into lists; plain strings from + # older listeners. + heard.append(transcription[0] if isinstance(transcription, (list, tuple)) + else str(transcription)) + return heard[0] if heard else "" + + +def main(argv=None): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument("--url", default="ws://127.0.0.1:8181/core") + parser.add_argument("--utterance", default="what time is it") + parser.add_argument("--expect-word", default="time", + help="a word the recognizer has to hear in --utterance") + parser.add_argument("--expect-tts", default=LOCAL_TTS, + help="the plugin the audio service has to name") + parser.add_argument("--wav", required=True, + help="where to write what the voice said") + parser.add_argument("--connect-timeout", type=float, default=30) + parser.add_argument("--ready-timeout", type=float, default=300, + help="the services restart onto local speech, and the " + "listener loads its model before it answers") + parser.add_argument("--listener-ready-timeout", type=float, default=60, + help="how long to give a listener that may have no " + "microphone to start with") + parser.add_argument("--reply-timeout", type=float, default=120) + args = parser.parse_args(argv) + + bus = None + try: + bus = Bus(args.url, args.connect_timeout) + wait_ready(bus, "audio", args.ready_timeout) + + started = time.monotonic() + wav_bytes, tts_id = speak(bus, args.utterance, args.reply_timeout) + print(f"voice : the audio service said {args.utterance!r} with {tts_id} " + f"in {time.monotonic() - started:.1f}s") + with open(args.wav, "wb") as out: + out.write(wav_bytes) + if tts_id != args.expect_tts: + raise Failure( + f"the running audio service speaks with {tts_id!r}, not {args.expect_tts!r}. " + f"mycroft.conf may name the local voice, but the service never loaded it." + ) + + try: + wait_ready(bus, "voice", args.listener_ready_timeout) + except Failure: + print("listener : not ready, so it is not answering the bus here (no " + "microphone to start with?)") + return LISTENER_UNAVAILABLE + + started = time.monotonic() + heard = transcribe(bus, wav_bytes, args.reply_timeout) + print(f"listener : heard {heard!r} in {time.monotonic() - started:.1f}s") + if args.expect_word not in heard.lower(): + raise Failure( + f"the voice said {args.utterance!r} and the running listener heard " + f"{heard!r}." + ) + print("the running services speak and hear on this device") + return 0 + except Failure as failure: + print(f"\nlocal speech check failed: {failure}", file=sys.stderr) + return 1 + finally: + if bus is not None: + bus.close() + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/test_local_speech_bus.py b/scripts/test_local_speech_bus.py new file mode 100644 index 00000000..e08d1547 --- /dev/null +++ b/scripts/test_local_speech_bus.py @@ -0,0 +1,175 @@ +"""The local speech check that asks the running services, against a fake messagebus. + +.github/scripts/assert_local_speech_bus.py is what tells a local speech install whose +services really loaded the local plugins from one whose mycroft.conf merely names them. +Each way that can go wrong is played here by a bus that answers in that one wrong way. +""" +import io +import subprocess +import sys +import tempfile +import time +import unittest +import wave +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +HARNESS = ROOT / ".github" / "scripts" / "assert_local_speech_bus.py" + +try: + import websockets # noqa: F401 (used by the subprocess server below) + HAVE_SERVER = True +except ImportError: + HAVE_SERVER = False + +try: + import websocket # noqa: F401 + HAVE_CLIENT = True +except ImportError: + HAVE_CLIENT = False + +SERVER = r""" +import asyncio, base64, io, json, sys, wave, websockets +MODE, PORT = sys.argv[1], int(sys.argv[2]) + +def voice(): + # What phoonnx produces: a mono 16-bit WAV at 22050 Hz. + out = io.BytesIO() + with wave.open(out, "wb") as wav: + wav.setnchannels(1) + wav.setsampwidth(2) + wav.setframerate(22050) + wav.writeframes(b"\x01\x00" * 2205) + return out.getvalue() + +async def handler(ws): + async for raw in ws: + m = json.loads(raw) + kind, data, ctx = m.get("type"), m.get("data") or {}, m.get("context") or {} + async def send(rt, payload, context=ctx): + await ws.send(json.dumps({"type": rt, "data": payload, "context": context})) + if kind.endswith(".is_ready"): + if kind == "mycroft.voice.is_ready" and MODE == "no_microphone": + continue + await send(kind + ".response", {"status": True}) + elif kind == "speak:b64_audio": + tts_id = "ovos-tts-plugin-server" if MODE == "never_reloaded" else "ovos-tts-plugin-phoonnx" + payload = {"audio": base64.b64encode(voice()).decode(), "listen": False, + "tts_id": tts_id, "utterance": data.get("utterance")} + # Someone else's speech first: it must not be taken for the answer. + await send("ovos.audio.speech", dict(payload, tts_id="other"), {"source": ["skills"]}) + topic = "speak:b64_audio.response" if MODE == "legacy_audio" else "ovos.audio.speech" + await send(topic, payload) + elif kind == "recognizer_loop:b64_transcribe": + audio = base64.b64decode(data.get("audio", "")) + # The listener reads raw frames at the rate it is told. A WAV header, or the + # wrong rate, is noise to it. + if audio.startswith(b"RIFF") or data.get("sample_rate") != 22050 or data.get("sample_width") != 2: + heard = "brr" + else: + heard = "" if MODE == "deaf_listener" else "what time is it" + await send("recognizer_loop:b64_transcribe.response", + {"transcriptions": [[heard, 0.9]], "lang": "en-US"}) + +async def main(): + async with websockets.serve(handler, "127.0.0.1", PORT): + print("ready", flush=True) + await asyncio.Future() + +asyncio.run(main()) +""" + + +class FakeBus: + """A messagebus whose audio service and listener answer correctly, or in one wrong way.""" + + _next_port = [8371] + + def __init__(self, mode="healthy"): + self.mode = mode + self.port = FakeBus._next_port[0] + FakeBus._next_port[0] += 1 + self.proc = None + + def __enter__(self): + self.proc = subprocess.Popen( + [sys.executable, "-c", SERVER, self.mode, str(self.port)], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + deadline = time.monotonic() + 15 + while time.monotonic() < deadline: + line = self.proc.stdout.readline() + if line.strip() == "ready": + return self + if self.proc.poll() is not None: + raise RuntimeError(f"fake bus died: {self.proc.stderr.read()}") + raise RuntimeError("fake bus never became ready") + + def __exit__(self, *exc): + if self.proc and self.proc.poll() is None: + self.proc.terminate() + try: + self.proc.wait(timeout=5) + except subprocess.TimeoutExpired: + self.proc.kill() + self.proc.wait() + if self.proc: + self.proc.stdout.close() + self.proc.stderr.close() + + +@unittest.skipUnless(HAVE_SERVER and HAVE_CLIENT, + "needs websockets (server) and websocket-client (harness)") +class RunningServicesCheck(unittest.TestCase): + + def check(self, mode): + with FakeBus(mode) as bus, tempfile.TemporaryDirectory() as tmp: + wav = Path(tmp) / "said.wav" + result = subprocess.run( + [sys.executable, str(HARNESS), "--url", f"ws://127.0.0.1:{bus.port}/core", + "--wav", str(wav), "--connect-timeout", "4", "--ready-timeout", "6", + "--listener-ready-timeout", "3", "--reply-timeout", "4"], + capture_output=True, text=True, timeout=90) + written = wav.read_bytes() if wav.exists() else b"" + return result.returncode, result.stdout + result.stderr, written + + def test_services_that_speak_and_hear_locally_pass(self): + code, output, _ = self.check("healthy") + self.assertEqual(code, 0, output) + self.assertIn("ovos-tts-plugin-phoonnx", output) + self.assertIn("heard 'what time is it'", output) + + def test_an_answer_on_the_legacy_topic_counts(self): + code, output, _ = self.check("legacy_audio") + self.assertEqual(code, 0, output) + + def test_an_audio_service_still_on_the_public_voice_fails(self): + # mycroft.conf names phoonnx, the service never reloaded it: the case that every + # check on the file passes. + code, output, _ = self.check("never_reloaded") + self.assertEqual(code, 1, output) + self.assertIn("ovos-tts-plugin-server", output) + self.assertIn("never loaded it", output) + + def test_a_listener_with_no_microphone_hands_the_audio_back(self): + # Exit 3 is the caller's cue to have the recognizer hear the file in a process of + # its own, so the file has to be the voice's own WAV. + code, output, written = self.check("no_microphone") + self.assertEqual(code, 3, output) + with wave.open(io.BytesIO(written)) as wav: + self.assertEqual(wav.getframerate(), 22050) + + def test_a_listener_that_hears_nothing_fails(self): + code, output, _ = self.check("deaf_listener") + self.assertEqual(code, 1, output) + self.assertIn("heard ''", output) + + def test_it_is_executable_and_documents_its_own_flags(self): + self.assertTrue(HARNESS.stat().st_mode & 0o111, "the harness must be executable") + result = subprocess.run([sys.executable, str(HARNESS), "--help"], + capture_output=True, text=True, timeout=30) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertIn("--expect-tts", result.stdout) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/bats/code_quality.bats b/tests/bats/code_quality.bats index 33e8a063..938f5fb7 100644 --- a/tests/bats/code_quality.bats +++ b/tests/bats/code_quality.bats @@ -5207,6 +5207,13 @@ for template in ('core-requirements.txt.j2', 'satellite-requirements.txt.j2'): # ovos_listener, and the uninstall takes the model volumes away. run grep -qF '.github/scripts/assert_local_speech.sh --containers' "$workflow" assert_success + + # The running services are asked too, not only the file: a mycroft.conf that + # names the local plugins passes every check on the file while services that + # never reloaded it stay on the public servers. + [ -x .github/scripts/assert_local_speech_bus.py ] + run grep -qF 'assert_local_speech_bus.py' .github/scripts/assert_local_speech.sh + assert_success run grep -qF 'the uninstall left the ${volume} volume behind' "$workflow" assert_success } From 372725c3171114aae6b2f5e72dd9c56531a61d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Tue, 6 Oct 2026 23:47:34 -0400 Subject: [PATCH 09/14] Keep an update of an install from before the speech question on public The speech screen preselected local whenever the hardware qualified. On an update of an install made before this question existed, there is no saved answer, so pressing Enter through the screens switched a working public device to local and downloaded gigabytes of models. An existing install with no saved answer now starts on public, which is what it was running. A new install still starts on local, and a saved answer wins either way. Co-Authored-By: Claude Opus 5.5 --- tests/bats/tui_navigation.bats | 29 +++++++++++++++++++++++++++++ tui/speech.sh | 9 ++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/tests/bats/tui_navigation.bats b/tests/bats/tui_navigation.bats index 873c395d..2683207f 100644 --- a/tests/bats/tui_navigation.bats +++ b/tests/bats/tui_navigation.bats @@ -766,6 +766,35 @@ function rendered_titles() { assert_equal "$(jq -r '.speech_engine' "$INSTALLER_STATE_FILE")" "public" } +@test "tui: updating an install from before the speech question keeps it public" { + # It ran on the public servers, and pressing Enter through an update must not + # switch it over and download gigabytes of models. + EXISTING_INSTANCE="true" + INSTANCE_TYPE="virtualenv" + export LOCAL_SPEECH_CAPABLE="true" + WHIPTAIL_PREFERRED_TAGS="alpha" + + # shellcheck source=tui/main.sh + source tui/main.sh + + run rendered_titles + assert_line "Open Voice OS Installation - Speech" + assert_equal "$SPEECH_ENGINE" "public" +} + +@test "tui: updating an install keeps the speech it chose" { + EXISTING_INSTANCE="true" + INSTANCE_TYPE="virtualenv" + export LOCAL_SPEECH_CAPABLE="true" + WHIPTAIL_PREFERRED_TAGS="alpha" + printf '{"speech_engine": "local"}\n' >"$INSTALLER_STATE_FILE" + + # shellcheck source=tui/main.sh + source tui/main.sh + + assert_equal "$SPEECH_ENGINE" "local" +} + @test "tui: the testing channel is never asked about speech" { export LOCAL_SPEECH_CAPABLE="true" diff --git a/tui/speech.sh b/tui/speech.sh index 3a4cad7a..44a76fed 100644 --- a/tui/speech.sh +++ b/tui/speech.sh @@ -5,8 +5,15 @@ source tui/navigation.sh source "tui/locales/$LOCALE/speech.sh" # The flow only shows this screen when local_speech_available, so both choices -# can actually be installed. Local is what the hardware was checked for. +# can actually be installed. Local is what the hardware was checked for, so a new +# install starts there. An existing install that has never answered predates this +# question and runs on the public servers: an update keeps it there unless the +# user picks local, rather than switching the device over, and downloading +# gigabytes of models, for someone pressing Enter through the screens. active_engine="local" +if [ "${EXISTING_INSTANCE:-false}" == "true" ]; then + active_engine="public" +fi if [ -f "$INSTALLER_STATE_FILE" ]; then saved_engine="$(jq -r '.speech_engine // ""' "$INSTALLER_STATE_FILE" 2>>"$LOG_FILE")" case "$saved_engine" in From 321f96994e78629d52d1bf8077e9721bcb7f621c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Wed, 7 Oct 2026 01:07:28 -0400 Subject: [PATCH 10/14] Let a containers uninstall remove the speech model volumes after a reboot The uninstall's label sweep spares anything whose name starts with "ovos_stt" or "ovos_tts", to protect STT and TTS servers people run themselves. The on-device model volumes, ovos_stt_models and ovos_tts_models, match those prefixes too, as does ovos-docker's ovos_tts_cache. They only went while the composition directory still existed, through compose down -v on the base file. That directory lives under /tmp, so an uninstall after a reboot left the models behind: gigabytes. The sweep now removes volumes ovos-docker's own compose files declare even when their names match an exclude. Server volumes such as ovos_tts_server_data are still spared. The new speech.bats case runs the real uninstall tasks against a fake docker_host_info result, with Docker out of reach. It fails without this. Co-Authored-By: Claude Opus 5.5 --- .../roles/ovos_containers/defaults/main.yml | 9 ++++ .../roles/ovos_containers/tasks/uninstall.yml | 3 +- tests/bats/speech.bats | 50 +++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/ansible/roles/ovos_containers/defaults/main.yml b/ansible/roles/ovos_containers/defaults/main.yml index 7aec356d..01600025 100644 --- a/ansible/roles/ovos_containers/defaults/main.yml +++ b/ansible/roles/ovos_containers/defaults/main.yml @@ -161,6 +161,15 @@ ovos_containers_uninstall_name_prefixes: # use an "ovos_" name prefix. ovos_containers_uninstall_name_excludes: >- {{ ovos_installer_uninstall_name_excludes | default(['ovos_stt', 'ovos_tts']) }} +# Volumes ovos-docker's own compose files declare whose names happen to match an +# exclude pattern: the on-device speech models (gigabytes) and the TTS cache. They +# belong to the stack, so the sweep removes them like its other volumes. Without +# this, they only went while the composition directory existed - an uninstall +# after a reboot left them behind. +ovos_containers_uninstall_volume_exceptions: + - ovos_stt_models + - ovos_tts_models + - ovos_tts_cache # Remove the images backing the removed containers. Follows the docker-compose # "remove_images" setting so both removal paths behave the same way. ovos_containers_uninstall_remove_images: >- diff --git a/ansible/roles/ovos_containers/tasks/uninstall.yml b/ansible/roles/ovos_containers/tasks/uninstall.yml index 65a1fe22..78664a41 100644 --- a/ansible/roles/ovos_containers/tasks/uninstall.yml +++ b/ansible/roles/ovos_containers/tasks/uninstall.yml @@ -154,10 +154,11 @@ _ovos_containers_uninstall_volumes_raw: >- {% set projects = ovos_containers_uninstall_project_names | default([]) %} {% set exclude_re = _ovos_containers_uninstall_exclude_re | trim %} + {% set exceptions = ovos_containers_uninstall_volume_exceptions | default([]) %} {% set ns = namespace(names=[]) %} {% for v in (ovos_containers_host_info.volumes | default([])) %} {% set labels = v.Labels | default({}) or {} %} - {% set excluded = exclude_re | length > 0 and v.Name is match(exclude_re) %} + {% set excluded = exclude_re | length > 0 and v.Name is match(exclude_re) and v.Name not in exceptions %} {% if labels.get(ovos_containers_uninstall_compose_project_label, '') in projects and not excluded %} {% set _ = ns.names.append(v.Name) %} {% endif %} diff --git a/tests/bats/speech.bats b/tests/bats/speech.bats index 20dc80b5..8e4f1146 100644 --- a/tests/bats/speech.bats +++ b/tests/bats/speech.bats @@ -425,3 +425,53 @@ PUBLIC_TTS='{"module": "ovos-tts-plugin-server", "ovos-tts-plugin-server": {"voi run cmp "$SPEECH_DIR/before" "$SPEECH_CONF" assert_success } + +@test "speech: a containers uninstall removes the model volumes, and still spares STT and TTS servers" { + if ! command -v ansible-playbook >/dev/null 2>&1; then + skip "ansible-playbook is not available" + fi + if ! ansible-doc -t module community.docker.docker_volume >/dev/null 2>&1; then + skip "the community.docker collection is not installed" + fi + + # The real uninstall tasks, with Docker out of reach: the sweep's selection runs + # against what docker_host_info would have listed, and nothing is removed. + local play + play="$(mktemp "${BATS_TEST_TMPDIR:-/tmp}/sweep.XXXXXX.yml")" + cat >"$play" <<'YAML' +- hosts: localhost + gather_facts: false + tasks: + - name: Run the containers uninstall + ansible.builtin.include_role: + name: ovos_containers + tasks_from: uninstall.yml + - name: Say what the sweep picked + ansible.builtin.debug: + msg: "VOLUMES={{ ovos_containers_uninstall_target_volumes | sort | join(',') }}" +YAML + + # The model volumes start "ovos_stt" and "ovos_tts", the prefixes that protect + # STT and TTS servers people run themselves. Removed only while the composition + # directory still existed, they outlived any uninstall after a reboot. + run ansible-playbook -i localhost, -c local "$play" -e '{ + "ansible_become": false, + "ovos_containers_docker_socket_path": "/nonexistent/docker.sock", + "ovos_containers_cmdline_path": "/nonexistent/cmdline.txt", + "ovos_installer_docker_compose_remove_volumes": false, + "ovos_containers_remove_directories": [], + "ovos_containers_repo_directories": [], + "ovos_containers_host_info": {"containers": [], "networks": [], "volumes": [ + {"Name": "ovos_stt_models", "Labels": {"com.docker.compose.project": "ovos"}}, + {"Name": "ovos_tts_models", "Labels": {"com.docker.compose.project": "ovos"}}, + {"Name": "ovos_tts_cache", "Labels": {"com.docker.compose.project": "ovos"}}, + {"Name": "ovos_models", "Labels": {"com.docker.compose.project": "ovos"}}, + {"Name": "ovos_tts_server_data", "Labels": {"com.docker.compose.project": "ovos"}}, + {"Name": "ovos_stt_whisper", "Labels": {"com.docker.compose.project": "ovos"}}, + {"Name": "somebody_else", "Labels": {"com.docker.compose.project": "other"}} + ]} + }' + rm -f "$play" + assert_success + assert_output --partial "VOLUMES=ovos_models,ovos_stt_models,ovos_tts_cache,ovos_tts_models" +} From 04ffa73ca311b6b1f04ad6771f558221b813284d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Wed, 7 Oct 2026 17:11:26 -0400 Subject: [PATCH 11/14] Count huggingface_hub's records for a local download as its bookkeeping The local recognizer downloads its model into a directory of its own, and huggingface_hub 2 still writes the revision it looked up into the shared cache: hub/models--istupakov--parakeet-tdt-0.6b-v3-onnx/refs/main, 40 bytes, no model. Not an OVOS name, it read as the user's model, so the uninstall kept the whole cache the install had created, and both local speech virtualenv jobs failed the comparison. A repository's refs and its record of files that do not exist are now bookkeeping; a model the user downloaded still has files of its own and still keeps the cache. Co-Authored-By: Claude Opus 5.5 --- ansible/roles/ovos_installer/defaults/main.yml | 5 ++++- tests/bats/uninstall_footprint.bats | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/ansible/roles/ovos_installer/defaults/main.yml b/ansible/roles/ovos_installer/defaults/main.yml index 1ba0f6dc..da2f93e3 100644 --- a/ansible/roles/ovos_installer/defaults/main.yml +++ b/ansible/roles/ovos_installer/defaults/main.yml @@ -43,7 +43,10 @@ ovos_installer_footprint_shared_dirs: owned: >- {{ ovos_installer_footprint_hf_models | map('regex_replace', '^', 'hub/') | list + ovos_installer_footprint_hf_models | map('regex_replace', '^', 'hub/.locks/') | list }} - bookkeeping: [".*", "xet", "hub/.locks", "hub/CACHEDIR.TAG", "hub/version.txt"] + # A repository's refs and its record of files that do not exist hold no model: + # huggingface_hub writes them even for a download into a directory of its own, + # which is how the local recognizer fetches its model. + bookkeeping: [".*", "xet", "hub/.locks", "hub/CACHEDIR.TAG", "hub/version.txt", "hub/*--*/refs", "hub/*--*/.no_exist"] store: hub/blobs owned_even_if_there_before: true - path: "{{ ovos_installer_user_home }}/.local/share/uv" diff --git a/tests/bats/uninstall_footprint.bats b/tests/bats/uninstall_footprint.bats index 4c9f3db2..3bbb82ce 100644 --- a/tests/bats/uninstall_footprint.bats +++ b/tests/bats/uninstall_footprint.bats @@ -377,6 +377,20 @@ YAML assert_output "hf_their_token" } +@test "footprint: what huggingface_hub records for a download elsewhere is its own bookkeeping" { + play record + install_side_effects + # The local recognizer downloads its model into a directory of its own, and + # huggingface_hub still writes the revision it looked up into the cache. + mkdir -p "$H/.cache/huggingface/hub/models--istupakov--parakeet-tdt-0.6b-v3-onnx/refs" + echo 0123456789abcdef0123456789abcdef01234567 \ + >"$H/.cache/huggingface/hub/models--istupakov--parakeet-tdt-0.6b-v3-onnx/refs/main" + + play restore + run find "$H" -mindepth 1 + assert_output "" +} + @test "footprint: a file in the cache's store stays while one of the user's models links to it" { play record install_side_effects From 8e607c399ed77b95ddf932bc4d541cd3ff7f8c9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Thu, 8 Oct 2026 06:50:20 -0400 Subject: [PATCH 12/14] Delete a rejected recognizer model only when this run downloaded it A candidate over the memory budget had its model deleted, to give the disk back. But the candidates can include the model an earlier install's local section already uses, and that one was not this run's to delete. Whether the model's directory existed is noted before the probe; only one this run created goes. The test stand-in now downloads during the probe, as the real plugin does. From CodeRabbit on #648. Co-Authored-By: Claude Opus 5.5 --- .../roles/ovos_config/files/speech_setup.py | 22 ++++++++++++----- scripts/test_speech_setup.py | 24 +++++++++++++++---- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/ansible/roles/ovos_config/files/speech_setup.py b/ansible/roles/ovos_config/files/speech_setup.py index 8e8ad7a0..d13043d0 100644 --- a/ansible/roles/ovos_config/files/speech_setup.py +++ b/ansible/roles/ovos_config/files/speech_setup.py @@ -156,14 +156,22 @@ def model_of(section): return options.get("model") or options.get("voice") or section["module"] -def forget_model(model): - """Delete a rejected recognizer model, so it does not hold the disk until uninstall.""" +def model_cache(model): + """Where the recognizer plugin keeps `model`, or None when that cannot be told.""" try: from ovos_utils.xdg_utils import xdg_data_home except ImportError: - return - shutil.rmtree(os.path.join(xdg_data_home(), "ovos_stt_plugin_onnxasr", model.replace("/", "--")), - ignore_errors=True) + return None + return os.path.join(xdg_data_home(), "ovos_stt_plugin_onnxasr", model.replace("/", "--")) + + +def forget_model(model, existed_before): + """Delete a rejected recognizer model this run downloaded, so it does not hold the + disk until uninstall. One that was there before this run is not this run's to + delete: an earlier install's local section can name the very model tried here.""" + path = model_cache(model) + if path and not existed_before: + shutil.rmtree(path, ignore_errors=True) def first_working(kind, candidates): @@ -173,6 +181,8 @@ def first_working(kind, candidates): if model in tried: continue tried.add(model) + cache = model_cache(model) if kind == "stt" else None + existed_before = bool(cache) and os.path.exists(cache) try: run = subprocess.run([sys.executable, "-c", TRY, kind, lang, json.dumps(section)], capture_output=True, text=True, timeout=CANDIDATE_TIMEOUT) @@ -188,7 +198,7 @@ def first_working(kind, candidates): peak = json.loads(lines[-1])["peak_mb"] if kind == "stt" and peak > budget_mb: notes.append(f"STT {model} ({origin}) needs {peak} MB, above the {budget_mb} MB budget") - forget_model(model) + forget_model(model, existed_before) continue if origin != "ovos-config's recommendation": notes.append(f"{kind.upper()} uses {model}, {origin}") diff --git a/scripts/test_speech_setup.py b/scripts/test_speech_setup.py index 095227c5..f248b10c 100644 --- a/scripts/test_speech_setup.py +++ b/scripts/test_speech_setup.py @@ -44,16 +44,21 @@ class AudioData: def __init__(self, frames, rate, width): self.frames = frames """, - # A model whose name starts with "broken" fails to load, one starting with "huge" - # holds 200 MB once loaded, one starting with "stalled" never finishes loading; - # anything else loads and hears nothing. + # Loading downloads the model where the plugin keeps it, as the real one does. A + # model whose name starts with "broken" then fails to load, one starting with + # "huge" holds 200 MB once loaded, one starting with "stalled" never finishes + # loading; anything else loads and hears nothing. "ovos_plugin_manager/stt.py": """ - import time + import os, time class Recognizer: def __init__(self, config): model = config.get("model", "") + cache = os.path.join(os.environ["STUB_XDG_DATA_HOME"], "ovos_stt_plugin_onnxasr", + model.replace("/", "--")) + os.makedirs(cache, exist_ok=True) + open(os.path.join(cache, "model.onnx"), "a").close() if model.startswith("broken"): raise RuntimeError("cannot load " + model) if model.startswith("stalled"): @@ -219,12 +224,21 @@ def test_a_recognizer_that_does_not_load_gives_way_to_the_plugins_own_model(self def test_a_recognizer_over_the_memory_budget_gives_way_and_is_deleted(self): self.recommends({"offline_stt/pt-pt.conf": stt("huge/whisper-medium-pt")}) downloaded = self.data / "ovos_stt_plugin_onnxasr" / "huge--whisper-medium-pt" - downloaded.mkdir(parents=True) result = self.setup("pt-pt", kinds="stt", budget=100, registry={"pt": "OpenVoiceOS/parakeet-pt"}) self.assertEqual(result["stt"][STT]["model"], "OpenVoiceOS/parakeet-pt") self.assertTrue(any("above the 100 MB budget" in note for note in result["notes"])) self.assertFalse(downloaded.exists()) + def test_a_recognizer_over_the_budget_that_was_there_before_is_kept(self): + # An earlier install's local section can name the very model tried here. + self.recommends({"offline_stt/pt-pt.conf": stt("huge/whisper-medium-pt")}) + earlier = self.data / "ovos_stt_plugin_onnxasr" / "huge--whisper-medium-pt" + earlier.mkdir(parents=True) + (earlier / "weights.onnx").write_text("an earlier download") + result = self.setup("pt-pt", kinds="stt", budget=100, registry={"pt": "OpenVoiceOS/parakeet-pt"}) + self.assertEqual(result["stt"][STT]["model"], "OpenVoiceOS/parakeet-pt") + self.assertEqual((earlier / "weights.onnx").read_text(), "an earlier download") + def test_a_voice_that_cannot_speak_gives_way_to_the_next_phoonnx_voice(self): self.recommends({"offline_male/es-es.conf": tts("mute/pipertts_es-ES_miro")}) result = self.setup("es-es", kinds="tts", voices={"es": ["mute/pipertts_es-ES_miro", "piper/es_ES-davefx-medium"]}) From 5dc053e5e3142e1d7cb6fc1b5f6cf2f61cdbb5a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Thu, 8 Oct 2026 07:39:09 -0400 Subject: [PATCH 13/14] Keep trying the plugins' own speech when a recommendation fails From CodeRabbit on #648, three ways a candidate was never tried: - A recommendation that could not be read or used raised inside the STT candidates and ended them, so the plugin's own model for the language was never tried and STT stayed public. It is noted and passed over now. - The same for TTS: phoonnx's own voices were never reached. - Candidates were told apart by model alone. A recommendation can ask for int8 weights a repository does not have, and the plugin's own choice of that same model without them was skipped as already tried. They are told apart by their whole section now. With the same model tried twice, whether its directory was there before is what this run first saw, not what the previous try left: what the int8 try downloaded is still this run's to delete. Co-Authored-By: Claude Opus 5.5 --- .../roles/ovos_config/files/speech_setup.py | 36 +++++++++++--- scripts/test_speech_setup.py | 48 ++++++++++++++++--- 2 files changed, 72 insertions(+), 12 deletions(-) diff --git a/ansible/roles/ovos_config/files/speech_setup.py b/ansible/roles/ovos_config/files/speech_setup.py index d13043d0..ee484155 100644 --- a/ansible/roles/ovos_config/files/speech_setup.py +++ b/ansible/roles/ovos_config/files/speech_setup.py @@ -118,9 +118,16 @@ def prefer_int8(stt): def stt_candidates(): - rec = recommended("offline_stt", "stt") + # A recommendation that cannot be read or used costs only itself: the plugin's + # own model for the language is still tried. + try: + rec = recommended("offline_stt", "stt") + rec = prefer_int8(rec) if rec else None + except Exception as error: + notes.append(f"STT recommendation could not be loaded: {type(error).__name__}: {error}") + rec = None if rec: - yield "ovos-config's recommendation", prefer_int8(rec) + yield "ovos-config's recommendation", rec try: from ovos_stt_plugin_onnxasr.defaults import resolve_model model = resolve_model(lang, {}) @@ -133,7 +140,12 @@ def stt_candidates(): def tts_candidates(): - rec = recommended(f"offline_{gender}", "tts") + # As for STT: phoonnx's own voices are still tried. + try: + rec = recommended(f"offline_{gender}", "tts") + except Exception as error: + notes.append(f"TTS recommendation could not be loaded: {type(error).__name__}: {error}") + rec = None if rec: yield "ovos-config's recommendation", rec try: @@ -174,15 +186,27 @@ def forget_model(model, existed_before): shutil.rmtree(path, ignore_errors=True) +# Whether each recognizer model's directory was there when this run first looked. +# The same model can be tried twice, as int8 and as fp32, and the second try must not +# take the first try's download for one that was there before. +there_at_first_sight = {} + + def first_working(kind, candidates): tried = set() for origin, section in candidates: model = model_of(section) - if model in tried: + # The same model with other settings is another candidate: a recommendation + # can ask for int8 weights a repository does not have, and the plugin's own + # choice of that model without them then still gets its try. + key = json.dumps(section, sort_keys=True) + if key in tried: continue - tried.add(model) + tried.add(key) cache = model_cache(model) if kind == "stt" else None - existed_before = bool(cache) and os.path.exists(cache) + if cache and cache not in there_at_first_sight: + there_at_first_sight[cache] = os.path.exists(cache) + existed_before = bool(cache) and there_at_first_sight[cache] try: run = subprocess.run([sys.executable, "-c", TRY, kind, lang, json.dumps(section)], capture_output=True, text=True, timeout=CANDIDATE_TIMEOUT) diff --git a/scripts/test_speech_setup.py b/scripts/test_speech_setup.py index f248b10c..4b9447bd 100644 --- a/scripts/test_speech_setup.py +++ b/scripts/test_speech_setup.py @@ -59,6 +59,9 @@ def __init__(self, config): model.replace("/", "--")) os.makedirs(cache, exist_ok=True) open(os.path.join(cache, "model.onnx"), "a").close() + # A repository without int8 weights: asking for them fails. + if "fp32only" in model and config.get("quantization") == "int8": + raise RuntimeError("no int8 weights in " + model) if model.startswith("broken"): raise RuntimeError("cannot load " + model) if model.startswith("stalled"): @@ -294,17 +297,50 @@ def test_without_public_servers_for_the_locale_the_fallback_keeps_its_defaults(s self.assertNotIn("ovos-stt-plugin-server", result["stt"]) def test_a_fault_while_choosing_costs_that_half_and_not_the_run(self): - # A recommendation that is not even JSON: the script must still answer, with the - # other half set up and the reason in the notes, rather than exit without a result. - self.recommends({"offline_male/en-us.conf": tts("miro")}) - broken = self.root / "stubs" / "ovos_config" / "recommends" / "offline_stt" / "en-us.conf" - broken.parent.mkdir(parents=True, exist_ok=True) - broken.write_text("{ not json") + # A section that names no plugin: the script must still answer, with the other + # half set up and the reason in the notes, rather than exit without a result. + self.recommends({"offline_stt/en-us.conf": {"stt": {"model": "parakeet"}}, + "offline_male/en-us.conf": tts("miro")}) result = self.setup("en-us") self.assertIsNone(result["stt"]) self.assertEqual(result["tts"][TTS]["voice"], "miro") self.assertTrue(any(note.startswith("STT could not be set up") for note in result["notes"])) + def broken_recommendation(self, folder): + broken = self.root / "stubs" / "ovos_config" / "recommends" / folder / "en-us.conf" + broken.parent.mkdir(parents=True, exist_ok=True) + broken.write_text("{ not json") + + def test_a_recommendation_that_cannot_be_read_gives_way_to_the_plugins_own_model(self): + self.recommends({"offline_male/en-us.conf": tts("miro")}) + self.broken_recommendation("offline_stt") + result = self.setup("en-us", registry={"en": "OpenVoiceOS/parakeet-en"}) + self.assertEqual(result["stt"][STT]["model"], "OpenVoiceOS/parakeet-en") + self.assertTrue(any(note.startswith("STT recommendation could not be loaded") for note in result["notes"])) + + def test_a_voice_recommendation_that_cannot_be_read_gives_way_to_phoonnx_voices(self): + self.recommends({}) + self.broken_recommendation("offline_male") + result = self.setup("en-us", kinds="tts", voices={"en": ["miro"]}) + self.assertEqual(result["tts"][TTS]["voice"], "miro") + self.assertTrue(any(note.startswith("TTS recommendation could not be loaded") for note in result["notes"])) + + def test_the_same_model_without_int8_is_tried_when_its_int8_weights_do_not_load(self): + self.recommends({"offline_stt/en-us.conf": stt("org/fp32only-en", quantization="int8")}) + result = self.setup("en-us", kinds="stt", registry={"en": "org/fp32only-en"}) + self.assertEqual(result["stt"][STT], {"model": "org/fp32only-en"}) + self.assertTrue(any("no int8 weights" in note for note in result["notes"])) + + def test_a_download_from_an_earlier_try_this_run_is_still_this_runs_to_delete(self): + # The int8 try downloads, then fails; the fp32 try of the same model is over the + # budget. What the int8 try downloaded is this run's, and goes. + self.recommends({"offline_stt/en-us.conf": stt("huge/fp32only-en", quantization="int8")}) + downloaded = self.data / "ovos_stt_plugin_onnxasr" / "huge--fp32only-en" + result = self.setup("en-us", kinds="stt", budget=100, registry={"en": "huge/fp32only-en"}) + self.assertIsNone(result["stt"]) + self.assertTrue(any("above the 100 MB budget" in note for note in result["notes"])) + self.assertFalse(downloaded.exists()) + def test_a_candidate_that_never_finishes_is_given_up_on(self): self.recommends({"offline_stt/en-us.conf": stt("stalled/parakeet")}) result = self.setup("en-us", kinds="stt", registry={"en": "OpenVoiceOS/parakeet-en"}, From 803c79857b4ad43e45b30d6366173ca286b83c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Trellu?= Date: Thu, 8 Oct 2026 16:32:25 -0400 Subject: [PATCH 14/14] Fix rejected speech model cleanup and disclose public fallback --- AUDIT.md | 28 ++++ FAQ.md | 24 ++++ MAINTENANCE_REPORT.md | 36 +++++ QUICK_FACTS.md | 18 +++ README.md | 11 +- SUGGESTIONS.md | 13 ++ .../roles/ovos_config/files/speech_setup.py | 8 +- docs/automation.md | 11 +- docs/index.md | 28 ++++ scripts/test_speech_setup.py | 24 ++++ tests/bats/locales.bats | 131 ++++++++++++++++++ translations/ca-es/strings.json | 6 +- translations/da/strings.json | 6 +- translations/de-de/strings.json | 6 +- translations/en-us/strings.json | 6 +- translations/es-es/strings.json | 6 +- translations/eu-es/strings.json | 6 +- translations/fr-fr/strings.json | 6 +- translations/gl-es/strings.json | 6 +- translations/hi-in/strings.json | 6 +- translations/it-it/strings.json | 6 +- translations/kab-dz/strings.json | 6 +- translations/nl-nl/strings.json | 6 +- translations/pl-pl/strings.json | 6 +- translations/pt-pt/strings.json | 6 +- tui/locales/ca-es/speech.sh | 12 +- tui/locales/ca-es/summary.sh | 2 +- tui/locales/da/speech.sh | 12 +- tui/locales/da/summary.sh | 2 +- tui/locales/de-de/speech.sh | 12 +- tui/locales/de-de/summary.sh | 2 +- tui/locales/en-us/speech.sh | 10 +- tui/locales/en-us/summary.sh | 2 +- tui/locales/es-es/speech.sh | 10 +- tui/locales/es-es/summary.sh | 2 +- tui/locales/eu-es/speech.sh | 10 +- tui/locales/eu-es/summary.sh | 2 +- tui/locales/fr-fr/speech.sh | 10 +- tui/locales/fr-fr/summary.sh | 2 +- tui/locales/gl-es/speech.sh | 10 +- tui/locales/gl-es/summary.sh | 2 +- tui/locales/hi-in/speech.sh | 10 +- tui/locales/hi-in/summary.sh | 2 +- tui/locales/it-it/speech.sh | 10 +- tui/locales/it-it/summary.sh | 2 +- tui/locales/kab-dz/speech.sh | 10 +- tui/locales/kab-dz/summary.sh | 2 +- tui/locales/nl-nl/speech.sh | 10 +- tui/locales/nl-nl/summary.sh | 2 +- tui/locales/pl-pl/speech.sh | 10 +- tui/locales/pl-pl/summary.sh | 2 +- tui/locales/pt-pt/speech.sh | 10 +- tui/locales/pt-pt/summary.sh | 2 +- tui/summary.sh | 2 +- 54 files changed, 442 insertions(+), 150 deletions(-) create mode 100644 AUDIT.md create mode 100644 FAQ.md create mode 100644 MAINTENANCE_REPORT.md create mode 100644 QUICK_FACTS.md create mode 100644 SUGGESTIONS.md create mode 100644 docs/index.md diff --git a/AUDIT.md b/AUDIT.md new file mode 100644 index 00000000..e1b569cb --- /dev/null +++ b/AUDIT.md @@ -0,0 +1,28 @@ +Last Edit: Codex (GPT-6) - 2026-10-08 - Motive: Record PR 648 findings, regression evidence and remaining validation limits. + +# Audit + +## 2026-10-08 — PR 648 follow-up + +- **Fixed: rejected STT downloads survived failed probes.** Timeout and unsuccessful + process exits now call `forget_model()` in + [`first_working()`](ansible/roles/ovos_config/files/speech_setup.py). + [`SpeechSetupTest.test_failed_probes_remove_only_new_downloads()`](scripts/test_speech_setup.py) + reproduces both failures, checks that existing caches survive and verifies the + successful fallback's files remain. The new assertions failed before the fix. +- **Fixed: local-speech privacy wording.** The selection and summary disclose + public fallback. The speech screen and [automation guide](docs/automation.md) + describe failed/empty recognition uploads and public speech when a local model + cannot run; they make no fully offline promise. +- **Verification:** `pytest -q scripts/test_speech_setup.py scripts/test_local_speech_bus.py` + passes **28 tests and 4 subtests**. **99 BATS tests pass with no skips**, including + all locales, navigation, real whiptail and speech Ansible tasks. Ruff, ShellCheck, + the offline contract checker and `git diff --check` pass. Display verification + is recorded in [the maintenance report](MAINTENANCE_REPORT.md). +- **Outstanding integration evidence:** the full matrix for reviewed head + `f10c2d5f` failed two container scenarios with skill-ID errors before completing + all speech checks. See [run 37778575537](https://github.com/OpenVoiceOS/ovos-installer/actions/runs/37778575537). + The affected launch IDs are not changed by this patch. A new matrix run and + Raspberry Pi 5 timing are required before claiming complete device coverage. + +This is a focused review of the changed paths, not a whole-repository security audit. diff --git a/FAQ.md b/FAQ.md new file mode 100644 index 00000000..e8e12d10 --- /dev/null +++ b/FAQ.md @@ -0,0 +1,24 @@ +Last Edit: Codex (GPT-6) - 2026-10-08 - Motive: Explain public fallback and safe rejected-model cleanup. + +# Frequently asked questions + +## Does local speech keep every recording offline? + +No. Failed or empty recognition sends that recording to public servers. If a +local recognizer or voice cannot run during setup, that component uses public +speech instead; the installer reports this. See [scenario settings](docs/automation.md#scenario-settings) +and [`with_public_fallback()`](ansible/roles/ovos_config/files/speech_setup.py). + +## What happens to a failed speech-model download? + +`first_working()` removes a rejected recognizer cache if the current run created +it, including after a crash or timeout. `forget_model()` preserves directories +that existed before the run. A successful retry keeps its working download. +See [implementation](ansible/roles/ovos_config/files/speech_setup.py) and +[`SpeechSetupTest.test_failed_probes_remove_only_new_downloads()`](scripts/test_speech_setup.py). + +## Do the automated tests install OVOS or download models? + +The speech setup unit tests use stub plugins and real child processes. They +exercise failure, timeout, retention and fallback behavior without an actual +installation. Real-device timing remains a separate validation step. diff --git a/MAINTENANCE_REPORT.md b/MAINTENANCE_REPORT.md new file mode 100644 index 00000000..9702fd45 --- /dev/null +++ b/MAINTENANCE_REPORT.md @@ -0,0 +1,36 @@ +Last Edit: Codex (GPT-6) - 2026-10-08 - Motive: Document cleanup and speech-fallback disclosure corrections for PR 648. + +# Maintenance report + +## 2026-10-08 — PR 648 review fixes + +- `first_working()` in [speech_setup.py](ansible/roles/ovos_config/files/speech_setup.py) + now cleans newly created recognizer caches after timeout and unsuccessful + probes, using the existing protection for caches present before the run. +- [Speech setup regressions](scripts/test_speech_setup.py) cover new/existing + caches under both failure paths, public fallback and a same-model retry that + succeeds. The new failure assertions were run against the old behavior first. +- Speech selection, summary and documentation now state that local speech has a + public fallback. All 14 locale catalogs and their generated screens are updated. +- Added a [documentation index](docs/index.md) and the workspace-required + maintenance references without changing runtime dependencies or installer pins. + +### Verification + +- Pytest: **28 passed, 4 subtests passed** for speech setup and bus checks. +- BATS: **99 passed, none skipped**, covering speech/Ansible integration, + navigation, all 14 locales, real whiptail and dialog sizing. +- Real English and French speech screens checked at 80×24 and 90×32; disclosures, + choices and navigation buttons remain visible. +- Repository-wide Ruff, ShellCheck on changed shell screens, offline contract + checks, Markdown local links and diff whitespace checks pass. +- No real-device installer or model downloads were run locally. + +### Transparency Report + +- **AI Model:** GPT-6 (Codex). +- **Actions Taken:** reproduced review findings, changed cleanup and localized + disclosure, added regression assertions, reviewed the diff, ran tests and wrote + these maintenance notes. +- **Oversight:** user requested the fixes; automated and AI review were performed. + No human translation sign-off or physical Raspberry Pi test is claimed. diff --git a/QUICK_FACTS.md b/QUICK_FACTS.md new file mode 100644 index 00000000..0e31c811 --- /dev/null +++ b/QUICK_FACTS.md @@ -0,0 +1,18 @@ +Last Edit: Codex (GPT-6) - 2026-10-08 - Motive: Record installer entry points and local-speech fallback behavior. + +# Quick facts + +| Field | Value | +| --- | --- | +| Package/repository | `OpenVoiceOS/ovos-installer`; shell/Ansible application, not a Python distribution | +| Version | Checked-out Git commit; `setup.sh` exports `INSTALLER_VERSION` from `git rev-parse --short=8 HEAD` | +| Entry points | `installer.sh` bootstrap; `setup.sh` orchestration; no Python package/plugin entry points | +| Speech selection | `speech_engine: public` or `local`; local includes public fallback | +| Local eligibility | Alpha, supported audio profile, capable 64-bit hardware and at least 7680 MiB RAM; see `utils/speech.sh` | +| Failed recognition | Failed or empty local recognition may send the recording to public servers | +| Failed model setup | The affected recognition or synthesis component uses public speech | +| Rejected STT downloads | New caches are removed after timeout, unsuccessful probe or excessive measured memory; existing caches are preserved | +| Test command | `pytest -q scripts/test_speech_setup.py scripts/test_local_speech_bus.py` | + +Source: [`first_working()`, `forget_model()` and `with_public_fallback()`](ansible/roles/ovos_config/files/speech_setup.py), +[`SpeechSetupTest`](scripts/test_speech_setup.py), [documentation](docs/index.md). diff --git a/README.md b/README.md index 5a10e16a..735b1037 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +Last Edit: Codex (GPT-6) - 2026-10-08 - Motive: Clarify that on-device speech includes a public-server fallback. + # Open Voice OS Installer **Your own voice assistant, on your own hardware.** Open source, @@ -24,10 +26,11 @@ speech, skills, services, the lot. - **Give it a brain.** An optional LLM fallback answers what the skills do not, pointed at whichever OpenAI-compatible endpoint you like — including one you host yourself. -- **Keep your voice at home.** On a Raspberry Pi 5 with 8 GB, or anything at - least as capable, speech recognition and the assistant's voice can run on the - device itself instead of on the community's public servers. Alpha channel - only, for now. +- **Run speech on your device.** On a Raspberry Pi 5 with 8 GB, or anything at + least as capable, recognition and the assistant's voice can run locally on + the alpha channel. Failed or empty recognition sends the recording to public + servers. If a local model cannot run, that part uses public speech instead. + This is not a fully offline mode. See [speech settings](docs/automation.md#scenario-settings). - **Put it in every room.** HiveMind satellites share a single assistant across several devices, so the Pi in the hallway and the one in the kitchen are the same assistant. diff --git a/SUGGESTIONS.md b/SUGGESTIONS.md new file mode 100644 index 00000000..4de38806 --- /dev/null +++ b/SUGGESTIONS.md @@ -0,0 +1,13 @@ +Last Edit: Codex (GPT-6) - 2026-10-08 - Motive: Record remaining local-speech validation opportunities. + +# Suggestions + +| Problem or opportunity | Proposed action | Expected impact | +| --- | --- | --- | +| Container end-to-end validation was interrupted by skill-ID errors | Resolve the image/package mismatch and rerun the exhaustive matrix, including ARM64 local speech | Confirms the installed services actually answer | +| CI runners do not establish Pi 5 latency | Install on a Pi 5 with 8 GB and time several real utterances, including an update | Confirms the advertised hardware floor and user experience | +| Localized fallback wording needs human review | Ask native speakers to review the 14 speech screens, especially Kabyle, Basque and Hindi | Makes recording uploads and public fallback understandable | + +Relevant evidence: [audit](AUDIT.md), +[`SpeechSetupTest`](scripts/test_speech_setup.py) and +[`first_working()`](ansible/roles/ovos_config/files/speech_setup.py). diff --git a/ansible/roles/ovos_config/files/speech_setup.py b/ansible/roles/ovos_config/files/speech_setup.py index ee484155..0a64ea2c 100644 --- a/ansible/roles/ovos_config/files/speech_setup.py +++ b/ansible/roles/ovos_config/files/speech_setup.py @@ -26,6 +26,7 @@ import shutil import subprocess import sys +from collections.abc import Iterable # Each candidate gets this long to download its model, load it and run once. That # is far more than any of them needs - it is there so that a download that stalls @@ -192,7 +193,8 @@ def forget_model(model, existed_before): there_at_first_sight = {} -def first_working(kind, candidates): +def first_working(kind: str, candidates: Iterable[tuple[str, dict]]) -> dict | None: + """Keep the first working candidate and remove only this run's rejected STT downloads.""" tried = set() for origin, section in candidates: model = model_of(section) @@ -213,11 +215,15 @@ def first_working(kind, candidates): except subprocess.TimeoutExpired: notes.append(f"{kind.upper()} {model} ({origin}) was given up on after " f"{CANDIDATE_TIMEOUT} seconds") + if kind == "stt": + forget_model(model, existed_before) continue lines = [line for line in run.stdout.splitlines() if line.startswith("{")] if run.returncode != 0 or not lines: reason = (run.stderr.strip().splitlines() or ["it did not start"])[-1] notes.append(f"{kind.upper()} {model} ({origin}) does not work here: {reason[:200]}") + if kind == "stt": + forget_model(model, existed_before) continue peak = json.loads(lines[-1])["peak_mb"] if kind == "stt" and peak > budget_mb: diff --git a/docs/automation.md b/docs/automation.md index 896efa0e..6b2d3b56 100644 --- a/docs/automation.md +++ b/docs/automation.md @@ -1,3 +1,5 @@ +Last Edit: Codex (GPT-6) - 2026-10-08 - Motive: Document public fallbacks for local speech scenarios. + # Automation Installing without answering questions, for scripted installs or several @@ -46,11 +48,18 @@ including one that runs speech recognition and the voice on a Raspberry Pi 5 | `llm.key` | API key for that endpoint, required with `features.llm` | | `llm.model` | Model name to use, required with `features.llm` | | `llm.persona` | System prompt for `ovos-persona`, required with `features.llm` | -| `speech_engine` | Where speech recognition and the voice run: `public` (the default) uses the community's public servers, which run on goodwill rather than as a production service and can go offline at any time; `local` runs them on the device. Local needs the `alpha` channel, a profile with audio (with `containers`, `ovos` or `listener`) and a Raspberry Pi 5 with 8 GB or an equivalent machine; anywhere else the installer says so and uses `public` | +| `speech_engine` | `public` (the default) uses community servers, which can go offline at any time. `local` runs speech on the device **with public fallback**: failed or empty recognition sends the recording to public servers, and recognition or voice synthesis uses public servers entirely if its local model cannot run. The installer reports any setup fallback. Local needs the `alpha` channel, a profile with audio (with `containers`, `ovos` or `listener`) and a Raspberry Pi 5 with 8 GB or an equivalent machine; anywhere else the installer uses `public`. This is not a fully offline mode. | | `raspberry_pi_tuning` | Maximum-performance tuning for a Pi, including an overclocking prompt | | `share_telemetry` | Share anonymous usage statistics — see [Telemetry](telemetry.md) | | `share_usage_telemetry` | Share detailed usage data — see [Telemetry](telemetry.md) | +Speech candidates are checked by `first_working()` in +[speech_setup.py](../ansible/roles/ovos_config/files/speech_setup.py). +Its `with_public_fallback()` preserves the locale's public server settings; +[speech.yml](../ansible/roles/ovos_config/tasks/speech.yml) applies the working +models and reports which parts remain public. Regression coverage lives in +[`SpeechSetupTest`](../scripts/test_speech_setup.py). + ## Environment variables For the settings that are not worth a screen of their own. With the `curl` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..8ba369cf --- /dev/null +++ b/docs/index.md @@ -0,0 +1,28 @@ +Last Edit: Codex (GPT-6) - 2026-10-08 - Motive: Index installer guides and local-speech maintenance evidence. + +# Installer documentation + +The installer selects a supported installation method, writes configuration and +manages OVOS services. Start with the [README](../README.md). + +| Guide | Scope | +| --- | --- | +| [Supported systems](supported-systems.md) | Hardware, operating systems and installation methods | +| [Automation](automation.md) | Scenarios, environment variables and public speech fallback | +| [macOS](macos.md) | Native macOS prerequisites and operation | +| [Services](services.md) | Starting, stopping and checking OVOS | +| [Terminal client](terminal-client.md) | Using OVOS without a microphone | +| [Troubleshooting](troubleshooting.md) | Diagnosing installation and runtime problems | +| [Telemetry](telemetry.md) | Optional data sharing | +| [Architecture](architecture.md) | Installer orchestration and roles | + +Local speech is selected by `first_working()` in +[speech_setup.py](../ansible/roles/ovos_config/files/speech_setup.py). +`forget_model()` removes rejected recognizer downloads only when they did not +exist before this run. `with_public_fallback()` retains the public server +settings; [speech.yml](../ansible/roles/ovos_config/tasks/speech.yml) applies them. +[`SpeechSetupTest`](../scripts/test_speech_setup.py) exercises these paths using +stub plugins and real child processes, without downloading models. + +Maintenance evidence: [audit](../AUDIT.md), [change log](../MAINTENANCE_REPORT.md), +[quick facts](../QUICK_FACTS.md), [FAQ](../FAQ.md) and [suggestions](../SUGGESTIONS.md). diff --git a/scripts/test_speech_setup.py b/scripts/test_speech_setup.py index 4b9447bd..9cb68fac 100644 --- a/scripts/test_speech_setup.py +++ b/scripts/test_speech_setup.py @@ -224,6 +224,28 @@ def test_a_recognizer_that_does_not_load_gives_way_to_the_plugins_own_model(self self.assertEqual(result["stt"][STT]["model"], "OpenVoiceOS/nvidia-ca-conformer") self.assertTrue(any("broken/ca-model" in note for note in result["notes"])) + def test_failed_probes_remove_only_new_downloads(self) -> None: + """Crashes and timeouts free new downloads but preserve earlier installs.""" + for failure in ("broken", "stalled"): + for preexisting in (False, True): + with self.subTest(failure=failure, preexisting=preexisting): + model = f"{failure}/{'existing' if preexisting else 'new'}" + self.recommends({"offline_stt/en-us.conf": stt(model)}) + rejected = self.data / "ovos_stt_plugin_onnxasr" / model.replace("/", "--") + if preexisting: + rejected.mkdir(parents=True) + (rejected / "weights.onnx").write_text("earlier installation") + + result = self.setup("en-us", kinds="stt", registry={"en": "working/fallback"}, + OVOS_SPEECH_CANDIDATE_TIMEOUT="1") + + self.assertEqual(result["stt"][STT]["model"], "working/fallback") + self.assertTrue((self.data / "ovos_stt_plugin_onnxasr" / + "working--fallback" / "model.onnx").is_file()) + self.assertEqual(rejected.exists(), preexisting) + if preexisting: + self.assertEqual((rejected / "weights.onnx").read_text(), "earlier installation") + def test_a_recognizer_over_the_memory_budget_gives_way_and_is_deleted(self): self.recommends({"offline_stt/pt-pt.conf": stt("huge/whisper-medium-pt")}) downloaded = self.data / "ovos_stt_plugin_onnxasr" / "huge--whisper-medium-pt" @@ -259,6 +281,7 @@ def test_when_nothing_works_both_halves_stay_public(self): result = self.setup("es-es", voices={"es": ["mute/es", "mute/other"]}) self.assertIsNone(result["stt"]) self.assertIsNone(result["tts"]) + self.assertFalse((self.data / "ovos_stt_plugin_onnxasr" / "broken--es").exists()) def test_int8_is_asked_for_where_the_repository_ships_it(self): model = "OpenVoiceOS/parakeet-rnnt-1.1b-es" @@ -330,6 +353,7 @@ def test_the_same_model_without_int8_is_tried_when_its_int8_weights_do_not_load( result = self.setup("en-us", kinds="stt", registry={"en": "org/fp32only-en"}) self.assertEqual(result["stt"][STT], {"model": "org/fp32only-en"}) self.assertTrue(any("no int8 weights" in note for note in result["notes"])) + self.assertTrue((self.data / "ovos_stt_plugin_onnxasr" / "org--fp32only-en" / "model.onnx").is_file()) def test_a_download_from_an_earlier_try_this_run_is_still_this_runs_to_delete(self): # The int8 try downloads, then fails; the fp32 try of the same model is over the diff --git a/tests/bats/locales.bats b/tests/bats/locales.bats index ac22f990..e869f8fa 100644 --- a/tests/bats/locales.bats +++ b/tests/bats/locales.bats @@ -415,3 +415,134 @@ function setup() { run grep -Eq '\b(default|provider|summary|voice-friendly|tuning)\b' "$file" assert_failure } + +@test "locales_speech_disclose_public_fallback_in_choices_and_summaries" { + run python3 - <<'PY' +import json +import subprocess +from pathlib import Path + +# Check what users see after sourcing the generated locales, including both +# fallback triggers. A translated heading alone must not satisfy disclosure. +expected = { + "en-us": ( + "with public fallback", "Downloads speech models", + "fails or returns no text, that recording is sent to public servers", + "recognition or synthesis cannot be set up, that part uses public servers", + "the installer reports it", "go offline at any time", + ), + "ca-es": ( + "amb servidors públics de reserva", "Baixa models de veu", + "falla o no retorna text, aquella gravació s'envia als servidors públics", + "reconeixement o la síntesi local, aquella part fa servir servidors públics", + "l'instal·lador ho indica", "deixar de funcionar en qualsevol moment", + ), + "da": ( + "med offentlige servere som reserve", "Henter talemodeller", + "fejler eller ikke giver tekst, sendes optagelsen til offentlige servere", + "genkendelse eller syntese ikke kan sættes op, bruger den del offentlige servere", + "installationsprogrammet oplyser det", "gå ned når som helst", + ), + "de-de": ( + "mit öffentlichen Servern als Ersatz", "Lädt Sprachmodelle herunter", + "fehlschlägt oder keinen Text liefert, wird die Aufnahme an öffentliche Server gesendet", + "Erkennung oder Synthese nicht einrichten, nutzt dieser Teil öffentliche Server", + "der Installer weist darauf hin", "jederzeit langsamer werden oder ausfallen", + ), + "es-es": ( + "con servidores públicos de respaldo", "Descarga modelos de voz", + "falla o no devuelve texto, esa grabación se envía a servidores públicos", + "reconocimiento o la síntesis local, esa parte usa servidores públicos", + "el instalador lo indica", "dejar de funcionar en cualquier momento", + ), + "eu-es": ( + "zerbitzari publikoak ordezko gisa", "Ahots-ereduak deskargatzen ditu", + "huts egiten badu edo testurik itzultzen ez badu, grabazioa zerbitzari publikoetara bidaltzen da", + "Ezagutza edo sintesi lokala ezin bada konfiguratu, zati horrek zerbitzari publikoak erabiltzen ditu", + "instalatzaileak horren berri ematen du", "edozein unetan moteldu edo gelditu daitezke", + ), + "fr-fr": ( + "avec des serveurs publics en secours", "Télécharge des modèles vocaux", + "échoue ou ne renvoie aucun texte, cet enregistrement est envoyé aux serveurs publics", + "reconnaissance ou la synthèse locale ne peut pas être configurée, cette partie utilise les serveurs publics", + "l'installateur le signale", "s'arrêter à tout moment", + ), + "gl-es": ( + "con servidores públicos de respaldo", "Descarga modelos de voz", + "falla ou non devolve texto, esa gravación envíase a servidores públicos", + "recoñecemento ou a síntese local, esa parte usa servidores públicos", + "o instalador indícao", "deixar de funcionar en calquera momento", + ), + "hi-in": ( + "सार्वजनिक सर्वरों की मदद से", "वाक् मॉडल डाउनलोड होते हैं", + "विफल हो या कोई टेक्स्ट न लौटाए, तो वह रिकॉर्डिंग सार्वजनिक सर्वरों पर भेजी जाती है", + "पहचान या संश्लेषण स्थापित नहीं हो पाता, तो उस हिस्से के लिए सार्वजनिक सर्वर इस्तेमाल होते हैं", + "इंस्टॉलर इसकी जानकारी देता है", "कभी भी बंद हो सकते हैं", + ), + "it-it": ( + "con server pubblici di riserva", "Scarica modelli vocali", + "fallisce o non restituisce testo, quella registrazione viene inviata ai server pubblici", + "riconoscimento o la sintesi locale, quella parte usa i server pubblici", + "l'installer lo segnala", "andare offline in qualsiasi momento", + ), + "kab-dz": ( + "s yiqeddacen izayazen d tallalt", "Ad d-yessader timudmin n taɣect", + "yecceḍ uɛqal adigan neɣ ur d-yerri ara aḍris, asekles-nni ad yettwazen ɣer yiqeddacen izayazen", + "aɛqal neɣ asuddes adigan, aḥric-nni ad yesseqdec iqeddacen izayazen", + "amesbeddi ad d-yefk talɣut ɣef waya", "ad ḥbesen melmi tebɣu tili", + ), + "nl-nl": ( + "met openbare servers als reserve", "Downloadt spraakmodellen", + "mislukt of geen tekst oplevert, gaat die opname naar openbare servers", + "herkenning of synthese niet kan worden ingesteld, gebruikt dat onderdeel openbare servers", + "het installatieprogramma meldt dit", "op elk moment traag worden of uitvallen", + ), + "pl-pl": ( + "z serwerami publicznymi w rezerwie", "Pobiera modele mowy", + "zawiedzie lub nie zwróci tekstu, nagranie jest wysyłane do serwerów publicznych", + "rozpoznawania lub syntezy, ta część używa serwerów publicznych", + "instalator o tym informuje", "przestać działać w każdej chwili", + ), + "pt-pt": ( + "com servidores públicos de reserva", "Descarrega modelos de voz", + "falhar ou não devolver texto, essa gravação é enviada para servidores públicos", + "reconhecimento ou a síntese local, essa parte usa servidores públicos", + "o instalador informa-o", "indisponíveis a qualquer momento", + ), +} +locale_paths = list(Path("translations").glob("*/strings.json")) +assert {path.parent.name for path in locale_paths} == set(expected) +for path in locale_paths: + locale = path.parent.name + data = json.loads(path.read_text()) + result = subprocess.run( + ["bash", "-euc", 'source "$1"; printf "%s\\0%s\\0" "$CONTENT" "$LOCAL_DESCRIPTION"; ' + 'source "$2"; printf "%s" "$SUMMARY_SPEECH_LOCAL"', "bash", + f"tui/locales/{locale}/speech.sh", f"tui/locales/{locale}/summary.sh"], + check=True, capture_output=True, text=True, + ) + content, choice, summary = result.stdout.split("\0") + fallback, *disclosures = expected[locale] + for field, value in (("content", content), ("choice", choice), ("summary", summary)): + assert fallback in value, f"{locale}: missing public fallback in {field}" + for disclosure in disclosures: + assert disclosure in content, f"{locale}: missing speech disclosure: {disclosure}" + assert choice == data["speech.sh"]["local_description"] + assert summary == data["summary.sh"]["speech_local"] +PY + assert_success +} + +@test "english_speech_locale_does_not_promise_private_or_offline_processing" { + run bash -euc ' + source tui/locales/en-us/speech.sh + printf "%s\n" "$CONTENT" "$LOCAL_DESCRIPTION" + source tui/locales/en-us/summary.sh + printf "%s\n" "$SUMMARY_SPEECH_LOCAL" + ' + assert_success + refute_output --partial "everything runs on this machine" + refute_output --partial "Your voice stays here" + refute_output --partial "nothing depends on the internet" + assert_output --partial "not a production service" +} diff --git a/translations/ca-es/strings.json b/translations/ca-es/strings.json index df172ace..db7fc813 100644 --- a/translations/ca-es/strings.json +++ b/translations/ca-es/strings.json @@ -58,14 +58,14 @@ }, "speech.sh": { "title": "Instal·lació de l'Open Voice OS - Veu", - "content": "L'Open Voice OS converteix el que dius en text (reconeixement de veu) i llegeix les respostes en veu alta (síntesi de veu). Aquest equip és prou potent per fer totes dues coses ell mateix.\n\n- local: tot s'executa en aquest equip. La teva veu es queda aquí i res no depèn d'internet ni de com d'ocupats estiguin els servidors. L'instal·lador baixa els models de veu del teu idioma, des d'uns quants centenars de megabytes fins a uns quants gigabytes, i el reconeixement manté ocupat el processador mentre parles.\n- public: la feina la fan servidors de la comunitat. S'ofereixen de bona voluntat, com a reserva i com a exemple de veu autoallotjada, no com un servei de producció: les respostes triguen més quan molta gent els fa servir alhora, i poden deixar de funcionar en qualsevol moment. No cal baixar res, però les teves gravacions i les respostes viatgen per internet i es processen en aquests servidors.\n\nSi el reconeixement local falla o no sent res, aquella gravació s'envia als servidors públics.\n\nSelecciona on es processa la veu, si us plau:", - "local_description": "Processar la veu en aquest equip", + "content": "L'Open Voice OS converteix la veu en text (reconeixement) i llegeix les respostes en veu alta (síntesi).\n\n - local: aquest equip amb servidors públics de reserva. Baixa models de veu (de centenars de megabytes a uns quants gigabytes). Si el reconeixement local falla o no retorna text, aquella gravació s'envia als servidors públics. Si no es pot configurar el reconeixement o la síntesi local, aquella part fa servir servidors públics; l'instal·lador ho indica.\n - public: els servidors de la comunitat processen les teves gravacions i les respostes de veu per internet. Aquests servidors de reserva, mantinguts per voluntaris, no són un servei de producció: poden anar lents o deixar de funcionar en qualsevol moment.\n\nSelecciona on es processa la veu:", + "local_description": "Aquest equip amb servidors públics de reserva", "public_description": "Servidors públics (sé que poden caure)" }, "summary.sh": { "title": "Instal·lació de l'Open VoiceOS - Resum", "content": "Gairebé heu acabat, aquí teniu un resum de les opcions que heu triat en instal·lar l'Open Voice OS:\n\n - Mètode: ${METHOD:-}\n - Versió: ${CHANNEL:-}\n - Perfil: ${PROFILE:-}\n - Habilitats: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Habilitats extra: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Veu: ${SPEECH_SUMMARY_STATE:-}\n - Afinació: ${TUNING_SUMMARY_STATE:-}\n\nLes opcions seleccionades durant el procés d'instal·lació de l'Open Voice OS s'han considerat acuradament per a adaptar el nostre sistema a les vostres necessitats i preferències úniques.\n\nUs sembla correcte aquest resum? Si no, selecciona ${BACK_BUTTON:-} per tornar enrere i fer canvis.", - "speech_local": "en aquest equip", + "speech_local": "en aquest equip amb servidors públics de reserva", "speech_public": "servidors públics", "speech_public_hardware": "servidors públics (local: Pi 5 de 8 GB o més)", "speech_public_setup": "servidors públics (local no disponible aquí)", diff --git a/translations/da/strings.json b/translations/da/strings.json index f9775956..f11a62a0 100644 --- a/translations/da/strings.json +++ b/translations/da/strings.json @@ -58,14 +58,14 @@ }, "speech.sh": { "title": "Open Voice OS Installation - Tale", - "content": "Open Voice OS omsætter det, du siger, til tekst (talegenkendelse) og læser sine svar højt (talesyntese). Denne maskine er kraftig nok til selv at klare begge dele.\n\n- local: alt kører på denne maskine. Din stemme bliver her, og intet afhænger af internettet eller af, hvor travlt serverne har. Installationsprogrammet henter talemodellerne til dit sprog, fra et par hundrede megabyte til et par gigabyte, og genkendelsen holder processoren beskæftiget, mens du taler.\n- public: fællesskabets servere klarer arbejdet. De drives af god vilje, som reserve og som eksempel på selvhostet talebehandling, ikke som en produktionstjeneste: svarene bliver langsommere, når mange bruger dem på samme tid, og serverne kan gå ned når som helst. Der skal ikke hentes noget, men dine optagelser og svarene sendes over internettet og behandles på de servere.\n\nHvis den lokale genkendelse fejler eller ikke hører noget, sendes netop den optagelse til de offentlige servere i stedet.\n\nVælg venligst, hvor tale skal behandles:", - "local_description": "Behandl tale på denne maskine", + "content": "Open Voice OS omsætter tale til tekst (genkendelse) og læser svar højt (syntese).\n\n - local: denne maskine med offentlige servere som reserve. Henter talemodeller (hundredvis af megabyte til nogle få gigabyte). Hvis lokal genkendelse fejler eller ikke giver tekst, sendes optagelsen til offentlige servere. Hvis lokal genkendelse eller syntese ikke kan sættes op, bruger den del offentlige servere; installationsprogrammet oplyser det.\n - public: fællesskabets servere behandler dine optagelser og talte svar over internettet. Disse frivilligt drevne reserveservere er ikke en produktionstjeneste: de kan blive langsomme eller gå ned når som helst.\n\nVælg, hvor tale skal behandles:", + "local_description": "Denne maskine med offentlige servere som reserve", "public_description": "Offentlige servere (jeg ved, de kan gå ned)" }, "summary.sh": { "title": "Open Voice OS Installation - Resume", "content": "Du er næsten færdig, her er en oversigt over de valg, du har truffet for at installere Open Voice OS:\n\n - Metode: ${METHOD:-}\n - Version: ${CHANNEL:-}\n - Profil: ${PROFILE:-}\n - Færdigheder: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Ekstra færdigheder: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Tale: ${SPEECH_SUMMARY_STATE:-}\n - Tuning: ${TUNING_SUMMARY_STATE:-}\n\nDe valg, der blev truffet under installationen af ​​Open Voice OS, er blevet nøje overvejet for at skræddersy vores system til dine unikke behov og præferencer.\n\nSer denne oversigt korrekt ud for dig? Hvis ikke, vælg ${BACK_BUTTON:-} for at gå tilbage og foretage ændringer.", - "speech_local": "på denne maskine", + "speech_local": "på denne maskine med offentlige servere som reserve", "speech_public": "offentlige servere", "speech_public_hardware": "offentlige servere (lokal: Pi 5 med 8 GB eller mere)", "speech_public_setup": "offentlige servere (lokal er ikke tilgængelig her)", diff --git a/translations/de-de/strings.json b/translations/de-de/strings.json index 15a836b9..45877bce 100644 --- a/translations/de-de/strings.json +++ b/translations/de-de/strings.json @@ -58,14 +58,14 @@ }, "speech.sh": { "title": "Open Voice OS-Installation - Sprachverarbeitung", - "content": "Open Voice OS wandelt Gesprochenes in Text um (Spracherkennung) und liest seine Antworten vor (Sprachsynthese). Dieser Rechner ist leistungsfähig genug, beides selbst zu erledigen.\n\n- local: Alles läuft auf diesem Rechner. Ihre Stimme bleibt hier, und nichts hängt vom Internet oder von der Auslastung der Server ab. Der Installer lädt die Sprachmodelle für Ihre Sprache herunter, von einigen hundert Megabyte bis zu ein paar Gigabyte, und die Erkennung lastet den Prozessor aus, während Sie sprechen.\n- public: Server der Community übernehmen die Arbeit. Sie werden aus gutem Willen betrieben, als Rückfallebene und als Beispiel für selbst gehostete Sprachverarbeitung, nicht als Produktionsdienst: Antworten dauern länger, wenn viele sie gleichzeitig nutzen, und die Server können jederzeit ausfallen. Es muss nichts heruntergeladen werden, aber Ihre Aufnahmen und die Antworten gehen über das Internet und werden auf diesen Servern verarbeitet.\n\nWenn die lokale Erkennung fehlschlägt oder nichts hört, wird genau diese Aufnahme stattdessen an die öffentlichen Server geschickt.\n\nBitte wählen Sie, wo die Sprache verarbeitet wird:", - "local_description": "Sprache auf diesem Rechner verarbeiten", + "content": "Open Voice OS wandelt Sprache in Text um (Erkennung) und liest Antworten vor (Synthese).\n\n - local: dieser Rechner mit öffentlichen Servern als Ersatz. Lädt Sprachmodelle herunter (hunderte Megabyte bis einige Gigabyte). Wenn die lokale Erkennung fehlschlägt oder keinen Text liefert, wird die Aufnahme an öffentliche Server gesendet. Lässt sich die lokale Erkennung oder Synthese nicht einrichten, nutzt dieser Teil öffentliche Server; der Installer weist darauf hin.\n - public: Community-Server verarbeiten Ihre Aufnahmen und gesprochenen Antworten über das Internet. Diese ehrenamtlich betriebenen Ersatzserver sind kein Produktionsdienst: Sie können jederzeit langsamer werden oder ausfallen.\n\nBitte wählen Sie, wo Sprache verarbeitet wird:", + "local_description": "Dieser Rechner mit öffentlichen Servern als Ersatz", "public_description": "Öffentliche Server (ich weiß, sie können ausfallen)" }, "summary.sh": { "title": "Open Voice OS Installation - Zusammenfassung", "content": "Fast fertig. Eine kurze Zusammenfassung:\n\n - Umgebung: ${METHOD:-}\n - Version: ${CHANNEL:-}\n - Profil: ${PROFILE:-}\n - Skills: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Extra-Skills: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Sprache: ${SPEECH_SUMMARY_STATE:-}\n - Tuning: ${TUNING_SUMMARY_STATE:-}\n\nDie Entscheidungen, die während des Installationsprozesses von Open Voice OS getroffen werden, wurden sorgfältig abgewogen, um unser System an Ihre individuellen Bedürfnisse und Vorlieben anzupassen.\n\nStimmen die Einstellungen? Falls nicht, wählen Sie ${BACK_BUTTON:-}, um zurückzugehen und Änderungen vorzunehmen.", - "speech_local": "auf diesem Rechner", + "speech_local": "auf diesem Rechner mit öffentlichen Servern als Ersatz", "speech_public": "öffentliche Server", "speech_public_hardware": "öffentliche Server (lokal: Pi 5 mit 8 GB oder besser)", "speech_public_setup": "öffentliche Server (lokal hier nicht verfügbar)", diff --git a/translations/en-us/strings.json b/translations/en-us/strings.json index e4169969..14f93d8f 100644 --- a/translations/en-us/strings.json +++ b/translations/en-us/strings.json @@ -58,8 +58,8 @@ }, "speech.sh": { "title": "Open Voice OS Installation - Speech", - "content": "Open Voice OS turns what you say into text (speech recognition) and reads its answers out loud (speech synthesis). This machine is powerful enough to do both itself.\n\n - local: everything runs on this machine. Your voice stays here, and nothing depends on the internet or on how busy the servers are. The installer downloads the speech models for your language, from a few hundred megabytes to a few gigabytes, and recognition keeps the processor busy while you talk.\n - public: community servers do the work. They run on goodwill, as a backup and as an example of self-hosted speech, not as a production service: answers take longer when many people use them, and the servers can go offline at any time. Nothing to download, but your recordings and the answers travel over the internet and are processed on those servers.\n\nWhen local recognition fails or hears nothing, that one recording is sent to the public servers instead.\n\nPlease select where speech is processed:", - "local_description": "Run speech on this machine", + "content": "Open Voice OS turns speech into text (recognition) and reads replies aloud (synthesis).\n\n - local: use this machine with public fallback. Downloads speech models (hundreds of megabytes to a few gigabytes). If local recognition fails or returns no text, that recording is sent to public servers. If local recognition or synthesis cannot be set up, that part uses public servers; the installer reports it.\n - public: community servers process your recordings and spoken replies over the internet. These volunteer-run backup servers are not a production service: they can slow down or go offline at any time.\n\nPlease select where speech is processed:", + "local_description": "This machine with public fallback", "public_description": "Public servers (I accept they can go offline)" }, "summary.sh": { @@ -70,7 +70,7 @@ "state_unsupported_profile": "selected (not supported for this profile)", "state_missing_url": "selected (missing URL; will be skipped)", "state_missing_configuration": "selected (missing configuration; will be skipped)", - "speech_local": "on this machine", + "speech_local": "on this machine with public fallback", "speech_public": "public servers", "speech_public_hardware": "public servers (local needs a Pi 5 with 8 GB or better)", "speech_public_setup": "public servers (local is not available here)", diff --git a/translations/es-es/strings.json b/translations/es-es/strings.json index 4a4303e8..56ad4694 100644 --- a/translations/es-es/strings.json +++ b/translations/es-es/strings.json @@ -58,14 +58,14 @@ }, "speech.sh": { "title": "Instalación de Open Voice OS - Voz", - "content": "Open Voice OS convierte lo que dice en texto (reconocimiento de voz) y lee sus respuestas en voz alta (síntesis de voz). Este equipo es lo bastante potente para hacer ambas cosas por sí mismo.\n\n- local: todo se ejecuta en este equipo. Su voz se queda aquí y nada depende de internet ni de lo ocupados que estén los servidores. El instalador descarga los modelos de voz de su idioma, desde unos cientos de megabytes hasta unos pocos gigabytes, y el reconocimiento mantiene ocupado el procesador mientras habla.\n- public: el trabajo lo hacen servidores de la comunidad. Se ofrecen de buena voluntad, como respaldo y como ejemplo de voz autoalojada, no como un servicio de producción: las respuestas tardan más cuando mucha gente los usa a la vez, y pueden dejar de funcionar en cualquier momento. No hay nada que descargar, pero sus grabaciones y las respuestas viajan por internet y se procesan en esos servidores.\n\nSi el reconocimiento local falla o no oye nada, esa grabación se envía a los servidores públicos.\n\nSeleccione dónde se procesa la voz:", - "local_description": "Procesar la voz en este equipo", + "content": "Open Voice OS convierte la voz en texto (reconocimiento) y lee las respuestas en voz alta (síntesis).\n\n - local: este equipo con servidores públicos de respaldo. Descarga modelos de voz (de cientos de megabytes a unos pocos gigabytes). Si el reconocimiento local falla o no devuelve texto, esa grabación se envía a servidores públicos. Si no se puede configurar el reconocimiento o la síntesis local, esa parte usa servidores públicos; el instalador lo indica.\n - public: los servidores de la comunidad procesan sus grabaciones y las respuestas de voz por internet. Estos servidores de respaldo, mantenidos por voluntarios, no son un servicio de producción: pueden ralentizarse o dejar de funcionar en cualquier momento.\n\nSeleccione dónde se procesa la voz:", + "local_description": "Este equipo con servidores públicos de respaldo", "public_description": "Servidores públicos (sé que pueden caerse)" }, "summary.sh": { "title": "Instalación de Open Voice OS - Resumen", "content": "¡Ya casi has terminado! Aquí tienes un resumen de las opciones que has elegido para instalar Open Voice OS:\n\n- Método: ${METHOD:-}\n- Versión: ${CHANNEL:-}\n- Perfil: ${PROFILE:-}\n- Habilidades: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n- Extras: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n- Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n- LLM: ${LLM_SUMMARY_STATE:-}\n- Voz: ${SPEECH_SUMMARY_STATE:-}\n- Ajustes: ${TUNING_SUMMARY_STATE:-}\n\nLas decisiones que has tomado durante el proceso de instalación de Open Voice OS han sido cuidadosamente consideradas para adaptar el sistema a tus necesidades y preferencias.\n\n¿Este resumen es correcto? Si no, selecciona ${BACK_BUTTON:-} para volver atrás y hacer cambios.", - "speech_local": "en este equipo", + "speech_local": "en este equipo con servidores públicos de respaldo", "speech_public": "servidores públicos", "speech_public_hardware": "servidores públicos (local: Pi 5 con 8 GB o más)", "speech_public_setup": "servidores públicos (local no disponible aquí)", diff --git a/translations/eu-es/strings.json b/translations/eu-es/strings.json index 090bea9b..2a332a17 100644 --- a/translations/eu-es/strings.json +++ b/translations/eu-es/strings.json @@ -57,14 +57,14 @@ }, "speech.sh": { "title": "Open Voice OS instalazioa - Ahotsa", - "content": "Open Voice OS-k esaten duzuna testu bihurtzen du (ahots-ezagutza) eta bere erantzunak ozen irakurtzen ditu (ahots-sintesia). Makina honek bi lanak berak egiteko adina indar du.\n\n - local: dena makina honetan exekutatzen da. Zure ahotsa hemen geratzen da, eta ezer ez dago Interneten edo zerbitzarien lan-kargaren mende. Instalatzaileak zure hizkuntzako ahots-ereduak deskargatzen ditu, ehunka megabytetik gigabyte gutxi batzuetara, eta ezagutzak prozesadorea lanpetuta mantentzen du hitz egiten duzun bitartean.\n - public: komunitatearen zerbitzariek egiten dute lana. Borondate onez eskaintzen dira, babes gisa eta norberak ostatatutako ahotsaren adibide gisa, ez produkzio-zerbitzu gisa: erantzunak motelago iristen dira jende askok aldi berean erabiltzen dituenean, eta edozein unetan gelditu daitezke. Ez dago ezer deskargatu beharrik, baina zure grabazioak eta erantzunak Internet bidez doaz eta zerbitzari horietan prozesatzen dira.\n\nEzagutza lokalak huts egiten badu edo ezer entzuten ez badu, grabazio hori zerbitzari publikoetara bidaltzen da.\n\nAukeratu non prozesatzen den ahotsa:", - "local_description": "Prozesatu ahotsa makina honetan", + "content": "Open Voice OS-k ahotsa testu bihurtzen du (ezagutza) eta erantzunak ozen irakurtzen ditu (sintesia).\n\n - local: makina hau, zerbitzari publikoak ordezko gisa erabiliz. Ahots-ereduak deskargatzen ditu (ehunka megabytetik gigabyte gutxi batzuetara). Ezagutza lokalak huts egiten badu edo testurik itzultzen ez badu, grabazioa zerbitzari publikoetara bidaltzen da. Ezagutza edo sintesi lokala ezin bada konfiguratu, zati horrek zerbitzari publikoak erabiltzen ditu; instalatzaileak horren berri ematen du.\n - public: komunitatearen zerbitzariek zure grabazioak eta ahots-erantzunak Internet bidez prozesatzen dituzte. Boluntarioek mantendutako ordezko zerbitzari hauek ez dira produkzio-zerbitzu bat: edozein unetan moteldu edo gelditu daitezke.\n\nAukeratu non prozesatzen den ahotsa:", + "local_description": "Makina hau, zerbitzari publikoak ordezko gisa", "public_description": "Zerbitzari publikoak (badakit eror daitezkeela)" }, "summary.sh": { "title": "Ireki Voice OS instalazioa - Laburpena", "content": "Ia amaitu duzu, hona hemen Open Voice OS instalatzeko egin dituzun aukeren laburpena:\n\n - Metodoa: ${METHOD:-}\n - Bertsioa: ${CHANNEL:-}\n - Profila: ${PROFILE:-}\n - Trebetasunak: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Trebetasun gehigarriak: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Ahotsa: ${SPEECH_SUMMARY_STATE:-}\n - Afinazioa: ${TUNING_SUMMARY_STATE:-}\n\nOpen Voice OS instalazio-prozesuan egindako aukerak arretaz aztertu dira gure sistema zure behar eta lehentasun berezietara egokitzeko.\n\nLaburpen hau zuzena iruditzen zaizu? Hala ez bada, hautatu ${BACK_BUTTON:-} atzera egiteko eta aldaketak egiteko.", - "speech_local": "makina honetan", + "speech_local": "makina honetan, zerbitzari publikoak ordezko gisa", "speech_public": "zerbitzari publikoak", "speech_public_hardware": "zerbitzari publikoak (lokala: 8 GB-ko Pi 5 edo hobea)", "speech_public_setup": "zerbitzari publikoak (lokala ez dago hemen)", diff --git a/translations/fr-fr/strings.json b/translations/fr-fr/strings.json index 740512d8..480bca3c 100644 --- a/translations/fr-fr/strings.json +++ b/translations/fr-fr/strings.json @@ -58,14 +58,14 @@ }, "speech.sh": { "title": "Open Voice OS Installation - Traitement de la parole", - "content": "Open Voice OS transforme ce que vous dites en texte (reconnaissance vocale) et lit ses réponses à voix haute (synthèse vocale). Cette machine est assez puissante pour faire les deux elle-même.\n\n- local : tout fonctionne sur cette machine. Votre voix reste ici, et rien ne dépend d'Internet ni de la charge des serveurs. L'installateur télécharge les modèles vocaux de votre langue, de quelques centaines de mégaoctets à quelques gigaoctets, et la reconnaissance occupe le processeur pendant que vous parlez.\n- public : des serveurs communautaires font le travail. Ils sont fournis de bonne volonté, comme solution de secours et comme exemple de traitement de la parole auto-hébergé, pas comme un service de production : les réponses ralentissent quand beaucoup de monde les utilise, et ils peuvent s'arrêter à tout moment. Rien à télécharger, mais vos enregistrements et les réponses passent par Internet et sont traités sur ces serveurs.\n\nSi la reconnaissance locale échoue ou n'entend rien, cet enregistrement-là est envoyé aux serveurs publics à la place.\n\nVeuillez sélectionner où la parole est traitée :", - "local_description": "Traiter la parole sur cette machine", + "content": "Open Voice OS transforme la parole en texte (reconnaissance) et lit les réponses à voix haute (synthèse).\n\n - local : cette machine avec des serveurs publics en secours. Télécharge des modèles vocaux (de quelques centaines de mégaoctets à quelques gigaoctets). Si la reconnaissance locale échoue ou ne renvoie aucun texte, cet enregistrement est envoyé aux serveurs publics. Si la reconnaissance ou la synthèse locale ne peut pas être configurée, cette partie utilise les serveurs publics ; l'installateur le signale.\n - public : les serveurs communautaires traitent vos enregistrements et les réponses vocales via Internet. Ces serveurs de secours, gérés par des bénévoles, ne sont pas un service de production : ils peuvent ralentir ou s'arrêter à tout moment.\n\nVeuillez sélectionner où la parole est traitée :", + "local_description": "Cette machine avec des serveurs publics en secours", "public_description": "Serveurs publics (je sais qu'ils peuvent tomber)" }, "summary.sh": { "title": "Open Voice OS Installation - Résumé", "content": "Vous y êtes presque ! Voici un résumé des choix que vous avez effectués pour l'installation d'Open Voice OS :\n\n - Méthode d'installation : ${METHOD:-}\n - Canal de déploiement : ${CHANNEL:-}\n - Profil d'installation : ${PROFILE:-}\n - Compétences par défaut : ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Compétences supplémentaires : ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant : ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM : ${LLM_SUMMARY_STATE:-}\n - Traitement de la parole : ${SPEECH_SUMMARY_STATE:-}\n - Réglages Raspberry Pi : ${TUNING_SUMMARY_STATE:-}\n\nLes choix effectués lors du processus d'installation d'Open Voice OS ont été soigneusement étudiés pour adapter notre système à vos besoins et préférences.\n\nEst-ce que tout cela vous semble correct ? Sinon, sélectionnez ${BACK_BUTTON:-} pour revenir en arrière et modifier vos choix.", - "speech_local": "sur cette machine", + "speech_local": "sur cette machine avec des serveurs publics en secours", "speech_public": "serveurs publics", "speech_public_hardware": "serveurs publics (local : Pi 5 8 Go minimum)", "speech_public_setup": "serveurs publics (local indisponible ici)", diff --git a/translations/gl-es/strings.json b/translations/gl-es/strings.json index ea9dacb2..199aba99 100644 --- a/translations/gl-es/strings.json +++ b/translations/gl-es/strings.json @@ -58,14 +58,14 @@ }, "speech.sh": { "title": "Instalación de Open Voice OS - Voz", - "content": "Open Voice OS converte o que dis en texto (recoñecemento de voz) e le as súas respostas en voz alta (síntese de voz). Este equipo é abondo potente para facer as dúas cousas el mesmo.\n\n - local: todo se executa neste equipo. A túa voz queda aquí e nada depende de internet nin de canto de ocupados estean os servidores. O instalador descarga os modelos de voz do teu idioma, desde uns centos de megabytes ata uns poucos gigabytes, e o recoñecemento mantén ocupado o procesador mentres falas.\n - public: o traballo fano servidores da comunidade. Ofrécense de boa vontade, como respaldo e como exemplo de voz autoaloxada, non como un servizo de produción: as respostas tardan máis cando moita xente os usa á vez, e poden deixar de funcionar en calquera momento. Non hai nada que descargar, pero as túas gravacións e as respostas viaxan por internet e procésanse neses servidores.\n\nSe o recoñecemento local falla ou non oe nada, esa gravación envíase aos servidores públicos.\n\nSelecciona onde se procesa a voz:", - "local_description": "Procesar a voz neste equipo", + "content": "Open Voice OS converte a voz en texto (recoñecemento) e le as respostas en voz alta (síntese).\n\n - local: este equipo con servidores públicos de respaldo. Descarga modelos de voz (de centos de megabytes a uns poucos gigabytes). Se o recoñecemento local falla ou non devolve texto, esa gravación envíase a servidores públicos. Se non se pode configurar o recoñecemento ou a síntese local, esa parte usa servidores públicos; o instalador indícao.\n - public: os servidores da comunidade procesan as túas gravacións e as respostas de voz por internet. Estes servidores de respaldo, mantidos por voluntarios, non son un servizo de produción: poden ir máis lentos ou deixar de funcionar en calquera momento.\n\nSelecciona onde se procesa a voz:", + "local_description": "Este equipo con servidores públicos de respaldo", "public_description": "Servidores públicos (sei que poden caer)" }, "summary.sh": { "title": "Instalación de Open Voice OS - Recapitulación", "content": "Estás a piques de rematar. Aquí tes un resumo das opcións que escolliches para instalar Open Voice OS:\n\n - Método: ${METHOD:-}\n - Versión: ${CHANNEL:-}\n - Perfil: ${PROFILE:-}\n - Habilidades: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Habilidades adicionais: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Voz: ${SPEECH_SUMMARY_STATE:-}\n - Axustes: ${TUNING_SUMMARY_STATE:-}\n\nAs decisións tomadas durante a instalación de Open Voice OS foron coidadosamente pensadas para adaptar o noso sistema ás túas necesidades e preferencias.\n\nÉ correcto este resumo? Se non, selecciona ${BACK_BUTTON:-} para volver atrás e facer cambios.", - "speech_local": "neste equipo", + "speech_local": "neste equipo con servidores públicos de respaldo", "speech_public": "servidores públicos", "speech_public_hardware": "servidores públicos (local: Pi 5 de 8 GB ou mellor)", "speech_public_setup": "servidores públicos (local non dispoñible aquí)", diff --git a/translations/hi-in/strings.json b/translations/hi-in/strings.json index e000d0f3..64f330a2 100644 --- a/translations/hi-in/strings.json +++ b/translations/hi-in/strings.json @@ -57,14 +57,14 @@ }, "speech.sh": { "title": "Open Voice OS Installation - आवाज़", - "content": "Open Voice OS आपकी कही बात को टेक्स्ट में बदलता है (वाक् पहचान) और अपने जवाब बोलकर सुनाता है (वाक् संश्लेषण)। यह मशीन ये दोनों काम ख़ुद करने के लिए काफ़ी शक्तिशाली है।\n\n - local: सब कुछ इसी मशीन पर चलता है। आपकी आवाज़ यहीं रहती है, और कुछ भी इंटरनेट या सर्वरों की व्यस्तता पर निर्भर नहीं करता। इंस्टॉलर आपकी भाषा के वाक् मॉडल डाउनलोड करता है, जो कुछ सौ मेगाबाइट से लेकर कुछ गीगाबाइट तक के होते हैं, और आपके बोलते समय पहचान प्रोसेसर को व्यस्त रखती है।\n - public: समुदाय के सर्वर यह काम करते हैं। इन्हें सद्भावना से चलाया जाता है, बैकअप के तौर पर और सेल्फ़-होस्टेड वाक् प्रसंस्करण के उदाहरण के रूप में, किसी प्रोडक्शन सेवा की तरह नहीं: जब बहुत से लोग एक साथ इनका उपयोग करते हैं तो जवाब धीमे हो जाते हैं, और ये सर्वर कभी भी बंद हो सकते हैं। कुछ डाउनलोड नहीं करना पड़ता, लेकिन आपकी रिकॉर्डिंग और जवाब इंटरनेट से होकर इन्हीं सर्वरों पर प्रोसेस होते हैं।\n\nअगर लोकल पहचान विफल हो जाए या कुछ न सुन पाए, तो वह रिकॉर्डिंग सार्वजनिक सर्वरों पर भेज दी जाती है।\n\nकृपया चुनें कि आवाज़ कहाँ प्रोसेस हो:", - "local_description": "इसी मशीन पर आवाज़ प्रोसेस करें", + "content": "Open Voice OS आवाज़ को टेक्स्ट में बदलता है (वाक् पहचान) और जवाब बोलकर सुनाता है (वाक् संश्लेषण)।\n\n - local: यह मशीन, ज़रूरत पड़ने पर सार्वजनिक सर्वरों की मदद से। वाक् मॉडल डाउनलोड होते हैं (कुछ सौ मेगाबाइट से कुछ गीगाबाइट तक)। अगर स्थानीय पहचान विफल हो या कोई टेक्स्ट न लौटाए, तो वह रिकॉर्डिंग सार्वजनिक सर्वरों पर भेजी जाती है। अगर स्थानीय पहचान या संश्लेषण स्थापित नहीं हो पाता, तो उस हिस्से के लिए सार्वजनिक सर्वर इस्तेमाल होते हैं; इंस्टॉलर इसकी जानकारी देता है।\n - public: समुदाय के सर्वर आपकी रिकॉर्डिंग और बोले जाने वाले जवाब इंटरनेट पर प्रोसेस करते हैं। स्वयंसेवकों द्वारा चलाए जा रहे ये वैकल्पिक सर्वर नियमित सेवा नहीं हैं: ये धीमे पड़ सकते हैं या कभी भी बंद हो सकते हैं।\n\nकृपया चुनें कि आवाज़ कहाँ प्रोसेस हो:", + "local_description": "यह मशीन, ज़रूरत पर सार्वजनिक सर्वरों की मदद से", "public_description": "सार्वजनिक सर्वर (ये कभी भी बंद हो सकते हैं)" }, "summary.sh": { "title": "Open Voice OS Installation - सारांश", "content": "आपका काम लगभग पूरा हो चुका है, ओपन वॉयस ओएस (Open Voice OS) स्थापित करने के लिए आपके द्वारा चुने गए विकल्पों का सारांश यहां दिया गया है:\n - विधि: ${METHOD:-}\n - संस्करण: ${CHANNEL:-}\n - प्रोफाइल: ${PROFILE:-}\n - कौशल: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - अतिरिक्त कौशल: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - आवाज़: ${SPEECH_SUMMARY_STATE:-}\n - ट्यूनिंग: ${TUNING_SUMMARY_STATE:-}\n\nओपन वॉयस ओएस (Open Voice OS) इंस्टॉलेशन प्रक्रिया के दौरान चुने गए विकल्पों पर हमारे सिस्टम (System) को आपकी विशिष्ट आवश्यकताओं और प्राथमिकताओं के अनुरूप बनाने के लिए सावधानीपूर्वक विचार किया गया है।\nक्या आपको यह सही लगता है? यदि नहीं, तो वापस जाने और बदलाव करने के लिए ${BACK_BUTTON:-} चुनें।", - "speech_local": "इसी मशीन पर", + "speech_local": "इस मशीन पर, ज़रूरत पर सार्वजनिक सर्वरों की मदद से", "speech_public": "सार्वजनिक सर्वर", "speech_public_hardware": "सार्वजनिक सर्वर (लोकल: 8 GB वाला Pi 5 या बेहतर)", "speech_public_setup": "सार्वजनिक सर्वर (यहाँ लोकल उपलब्ध नहीं)", diff --git a/translations/it-it/strings.json b/translations/it-it/strings.json index 22746fb3..f85dd236 100644 --- a/translations/it-it/strings.json +++ b/translations/it-it/strings.json @@ -58,14 +58,14 @@ }, "speech.sh": { "title": "Installazione di Open Voice OS - Voce", - "content": "Open Voice OS trasforma ciò che dici in testo (riconoscimento vocale) e legge le risposte ad alta voce (sintesi vocale). Questa macchina è abbastanza potente da fare entrambe le cose da sola.\n\n - local: tutto gira su questa macchina. La tua voce resta qui e nulla dipende da internet o da quanto sono occupati i server. L'installer scarica i modelli vocali per la tua lingua, da qualche centinaio di megabyte a qualche gigabyte, e il riconoscimento tiene occupato il processore mentre parli.\n - public: il lavoro lo fanno server della comunità. Sono offerti per buona volontà, come riserva e come esempio di voce in self-hosting, non come servizio di produzione: le risposte rallentano quando molte persone li usano contemporaneamente, e possono andare offline in qualsiasi momento. Niente da scaricare, ma le tue registrazioni e le risposte viaggiano su internet e vengono elaborate su quei server.\n\nSe il riconoscimento locale fallisce o non sente nulla, quella registrazione viene inviata ai server pubblici.\n\nSeleziona dove elaborare la voce:", - "local_description": "Elaborare la voce su questa macchina", + "content": "Open Voice OS trasforma la voce in testo (riconoscimento) e legge le risposte ad alta voce (sintesi).\n\n - local: questa macchina con server pubblici di riserva. Scarica modelli vocali (da centinaia di megabyte a pochi gigabyte). Se il riconoscimento locale fallisce o non restituisce testo, quella registrazione viene inviata ai server pubblici. Se non è possibile configurare il riconoscimento o la sintesi locale, quella parte usa i server pubblici; l'installer lo segnala.\n - public: i server della comunità elaborano le tue registrazioni e le risposte vocali tramite internet. Questi server di riserva, gestiti da volontari, non sono un servizio di produzione: possono rallentare o andare offline in qualsiasi momento.\n\nSeleziona dove elaborare la voce:", + "local_description": "Questa macchina con server pubblici di riserva", "public_description": "Server pubblici (so che possono andare offline)" }, "summary.sh": { "title": "Installazione di Open Voice OS - Riassunto", "content": "Abbiamo quasi finito. Qui c'è un riassunto delle opzioni scelte per installare Open Voice OS:\n\n - Distribuzione: ${METHOD:-}\n - Versione: ${CHANNEL:-}\n - Profilo: ${PROFILE:-}\n - Competenze: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Competenze extra: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Voce: ${SPEECH_SUMMARY_STATE:-}\n - Ottimizzazione: ${TUNING_SUMMARY_STATE:-}\n\nLe decisioni prese durante il processo di installazione di Open Voice OS sono state attentamente valutate per personalizzare il nostro sistema in base alle tue esigenze e preferenze individuali.\n\nLe impostazioni sono corrette? In caso contrario, seleziona ${BACK_BUTTON:-} per tornare indietro e apportare modifiche.", - "speech_local": "su questa macchina", + "speech_local": "su questa macchina con server pubblici di riserva", "speech_public": "server pubblici", "speech_public_hardware": "server pubblici (locale: Pi 5 con 8 GB o superiore)", "speech_public_setup": "server pubblici (locale non disponibile qui)", diff --git a/translations/kab-dz/strings.json b/translations/kab-dz/strings.json index f9c2875e..49359968 100644 --- a/translations/kab-dz/strings.json +++ b/translations/kab-dz/strings.json @@ -56,8 +56,8 @@ }, "speech.sh": { "title": "Asbeddi n Open Voice OS - Taɣect", - "content": "Open Voice OS yettbeddil ayen i d-tenniḍ ɣer uḍris (aɛqal n taɣect) yerna yeqqar-d tiririyin-is s taɣect (asuddes n taɣect). Tamacint-a tezmer ad txeddem snat-a s yiman-is.\n\n - local: kullec ad iteddu ɣef tmacint-a. Taɣect-ik ad teqqim da, ulac ayen icudden ɣer Internet neɣ ɣer yiqeddacen. Amesbeddi ad d-yessader timudmin n taɣect n tutlayt-ik, seg kra n yimiḍ n MB alamma d kra n GB, yerna aɛqal ad yesseqdec amesbadu mi ara tettmeslayeḍ.\n - public: d iqeddacen n temɣiwant ara ixedmen axeddim. Ttwafken-d s lebɣi, am tarrayt n uɛiwen d umedya n taɣect i yesbedden yiman-is, maci am umeẓlu n tfalit: tiririyin ad ẓẓayent mi ara ten-sqedcen aṭas n medden ɣef tikkelt, yerna zemren ad ḥbesen melmi tebɣu tili. Ulac ayen ara d-tessadreḍ, maca iseklasen-ik d tririyin ad ɛeddin s Internet u ad ttwasenfaren ɣef yiqeddacen-a.\n\nMa yecceḍ uɛqal adigan neɣ ur yesla i kra, asekles-nni ad yettwazen ɣer yiqeddacen izayazen.\n\nTtxil-k fren anida ara tettwasenfar taɣect:", - "local_description": "Taɣect ɣef tmacint-a", + "content": "Open Voice OS yettbeddil taɣect ɣer uḍris (aɛqal) yerna yeqqar-d tiririyin s taɣect (asuddes).\n\n - local: tamacint-a s yiqeddacen izayazen d tallalt. Ad d-yessader timudmin n taɣect (seg yimiḍ n MB alamma d kra n GB). Ma yecceḍ uɛqal adigan neɣ ur d-yerri ara aḍris, asekles-nni ad yettwazen ɣer yiqeddacen izayazen. Ma ur yezmir ara ad yesbedd aɛqal neɣ asuddes adigan, aḥric-nni ad yesseqdec iqeddacen izayazen; amesbeddi ad d-yefk talɣut ɣef waya.\n - public: iqeddacen n temɣiwant senfaren iseklasen-ik d tririyin n taɣect s Internet. Iqeddacen-a n tallalt ttwasefraken s lebɣi, mačči d ameẓlu n tfalit: zemren ad ẓẓayen neɣ ad ḥbesen melmi tebɣu tili.\n\nTtxil-k fren anida ara tettwasenfar taɣect:", + "local_description": "Tamacint-a s yiqeddacen izayazen d tallalt", "public_description": "Iqeddacen izayazen (ẓriɣ zemren ad ḥbesen)" }, "summary.sh": { @@ -68,7 +68,7 @@ "state_unsupported_profile": "yettawfran (ur yettwasefrek ara deg umaɣnu-a)", "state_missing_url": "yettwafran (txuṣṣ URL; as yettwazgal)", "state_missing_configuration": "yettwafran (txuṣṣ tawila; as yettwazgal)", - "speech_local": "ɣef tmacint-a", + "speech_local": "ɣef tmacint-a s yiqeddacen izayazen d tallalt", "speech_public": "iqeddacen izayazen", "speech_public_hardware": "iqeddacen izayazen (adigan: Pi 5 s 8 GB neɣ ugar)", "speech_public_setup": "iqeddacen izayazen (adigan ulac-it dagi)", diff --git a/translations/nl-nl/strings.json b/translations/nl-nl/strings.json index 6042fcf6..82d466e5 100644 --- a/translations/nl-nl/strings.json +++ b/translations/nl-nl/strings.json @@ -58,14 +58,14 @@ }, "speech.sh": { "title": "Open Voice OS Installatie - Spraak", - "content": "Open Voice OS zet wat je zegt om in tekst (spraakherkenning) en leest zijn antwoorden voor (spraaksynthese). Deze machine is krachtig genoeg om beide zelf te doen.\n\n- local: alles draait op deze machine. Je stem blijft hier, en niets hangt af van internet of van hoe druk de servers zijn. Het installatieprogramma downloadt de spraakmodellen voor je taal, van een paar honderd megabyte tot een paar gigabyte, en de herkenning houdt de processor bezig terwijl je praat.\n- public: servers van de community doen het werk. Ze draaien uit goede wil, als reserve en als voorbeeld van zelf gehoste spraakverwerking, niet als productiedienst: antwoorden duren langer wanneer veel mensen ze tegelijk gebruiken, en de servers kunnen op elk moment uitvallen. Je hoeft niets te downloaden, maar je opnames en de antwoorden gaan via internet en worden op die servers verwerkt.\n\nAls de lokale herkenning mislukt of niets hoort, wordt die ene opname naar de openbare servers gestuurd.\n\nSelecteer waar spraak wordt verwerkt:", - "local_description": "Spraak op deze machine verwerken", + "content": "Open Voice OS zet spraak om in tekst (herkenning) en leest antwoorden voor (synthese).\n\n - local: deze machine met openbare servers als reserve. Downloadt spraakmodellen (honderden megabytes tot enkele gigabytes). Als lokale herkenning mislukt of geen tekst oplevert, gaat die opname naar openbare servers. Als lokale herkenning of synthese niet kan worden ingesteld, gebruikt dat onderdeel openbare servers; het installatieprogramma meldt dit.\n - public: servers van de community verwerken je opnames en gesproken antwoorden via internet. Deze reserveservers draaien dankzij vrijwilligers en zijn geen productiedienst: ze kunnen op elk moment traag worden of uitvallen.\n\nSelecteer waar spraak wordt verwerkt:", + "local_description": "Deze machine met openbare servers als reserve", "public_description": "Openbare servers (ik weet dat ze kunnen uitvallen)" }, "summary.sh": { "title": "OpenVoice OS Installatie - Samenvatting", "content": "Bijna klaar. Een korte samenvatting:\n\n - Omgeving: ${METHOD:-}\n - Versie: ${CHANNEL:-}\n - Profiel: ${PROFILE:-}\n - Skills: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Extra skills: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Spraak: ${SPEECH_SUMMARY_STATE:-}\n - Tuning: ${TUNING_SUMMARY_STATE:-}\n\nDe beslissingen die zijn genomen tijdens het installatieproces van OpenVoice OS zijn zorgvuldig gemaakt om ons systeem aan te passen aan je individuele behoeften en voorkeuren.\n\nZijn de instellingen zo in orde? Zo niet, kies ${BACK_BUTTON:-} om terug te gaan en wijzigingen te maken.", - "speech_local": "op deze machine", + "speech_local": "op deze machine met openbare servers als reserve", "speech_public": "openbare servers", "speech_public_hardware": "openbare servers (lokaal: Pi 5 met 8 GB of beter)", "speech_public_setup": "openbare servers (lokaal hier niet beschikbaar)", diff --git a/translations/pl-pl/strings.json b/translations/pl-pl/strings.json index 6c8e51cf..528f270f 100644 --- a/translations/pl-pl/strings.json +++ b/translations/pl-pl/strings.json @@ -58,14 +58,14 @@ }, "speech.sh": { "title": "Instalacja Open Voice OS - Mowa", - "content": "Open Voice OS zamienia to, co mówisz, na tekst (rozpoznawanie mowy) i czyta swoje odpowiedzi na głos (synteza mowy). Ten komputer jest na tyle wydajny, żeby robić jedno i drugie samodzielnie.\n\n- local: wszystko działa na tym komputerze. Twój głos zostaje tutaj i nic nie zależy od internetu ani od obciążenia serwerów. Instalator pobiera modele mowy dla Twojego języka, od kilkuset megabajtów do kilku gigabajtów, a rozpoznawanie obciąża procesor, gdy mówisz.\n- public: pracę wykonują serwery społeczności. Działają w dobrej wierze, jako zapas i przykład samodzielnie hostowanego przetwarzania mowy, a nie jako usługa produkcyjna: odpowiedzi zwalniają, gdy korzysta z nich wiele osób naraz, a serwery w każdej chwili mogą przestać działać. Nic nie trzeba pobierać, ale Twoje nagrania i odpowiedzi trafiają przez internet na te serwery i tam są przetwarzane.\n\nJeśli lokalne rozpoznawanie zawiedzie lub nic nie usłyszy, to nagranie zostanie wysłane do serwerów publicznych.\n\nWybierz, gdzie ma być przetwarzana mowa:", - "local_description": "Przetwarzaj mowę na tym komputerze", + "content": "Open Voice OS zamienia mowę na tekst (rozpoznawanie) i czyta odpowiedzi na głos (synteza).\n\n - local: ten komputer z serwerami publicznymi w rezerwie. Pobiera modele mowy (od setek megabajtów do kilku gigabajtów). Jeśli lokalne rozpoznawanie zawiedzie lub nie zwróci tekstu, nagranie jest wysyłane do serwerów publicznych. Jeśli nie da się skonfigurować lokalnego rozpoznawania lub syntezy, ta część używa serwerów publicznych; instalator o tym informuje.\n - public: serwery społeczności przetwarzają Twoje nagrania i odpowiedzi głosowe przez internet. Te zapasowe serwery prowadzone przez wolontariuszy nie są usługą produkcyjną: mogą zwolnić lub przestać działać w każdej chwili.\n\nWybierz, gdzie ma być przetwarzana mowa:", + "local_description": "Ten komputer z serwerami publicznymi w rezerwie", "public_description": "Serwery publiczne (wiem, że mogą przestać działać)" }, "summary.sh": { "title": "Instalacja Open Voice OS – Podsumowanie", "content": "Już prawie skończyłeś, oto podsumowanie wyborów dokonanych podczas instalacji Open Voice OS:\n\n- Metoda: ${METHOD:-}\n- Wersja: ${CHANNEL:-}\n- Profil: ${PROFILE:-}\n- Umiejętności: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n- Dodatkowe umiejętności: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n- Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n- LLM: ${LLM_SUMMARY_STATE:-}\n- Mowa: ${SPEECH_SUMMARY_STATE:-}\n- Strojenie: ${TUNING_SUMMARY_STATE:-}\n\nWybory dokonane podczas instalacji Open Voice OS zostały starannie rozważone, aby dostosować nasz system do Twoich unikalnych potrzeb i preferencji.\n\nCzy to podsumowanie wydaje Ci się poprawne? Jeśli nie, wybierz ${BACK_BUTTON:-}, aby wrócić i wprowadzić zmiany.", - "speech_local": "na tym komputerze", + "speech_local": "na tym komputerze z serwerami publicznymi w rezerwie", "speech_public": "serwery publiczne", "speech_public_hardware": "serwery publiczne (lokalnie: Pi 5 z 8 GB lub lepszy)", "speech_public_setup": "serwery publiczne (tryb lokalny tu niedostępny)", diff --git a/translations/pt-pt/strings.json b/translations/pt-pt/strings.json index a9dcadf0..3f147349 100644 --- a/translations/pt-pt/strings.json +++ b/translations/pt-pt/strings.json @@ -58,14 +58,14 @@ }, "speech.sh": { "title": "Open Voice OS Instalação - Voz", - "content": "O Open Voice OS converte o que diz em texto (reconhecimento de voz) e lê as respostas em voz alta (síntese de voz). Esta máquina é suficientemente potente para fazer as duas coisas sozinha.\n\n - local: tudo corre nesta máquina. A sua voz fica aqui e nada depende da internet nem da ocupação dos servidores. O instalador descarrega os modelos de voz do seu idioma, de algumas centenas de megabytes a alguns gigabytes, e o reconhecimento mantém o processador ocupado enquanto fala.\n - public: o trabalho é feito por servidores da comunidade. São disponibilizados por boa vontade, como reserva e como exemplo de voz auto-alojada, não como um serviço de produção: as respostas demoram mais quando muitas pessoas os usam ao mesmo tempo, e podem ficar indisponíveis a qualquer momento. Não há nada para descarregar, mas as suas gravações e as respostas viajam pela internet e são processadas nesses servidores.\n\nSe o reconhecimento local falhar ou não ouvir nada, essa gravação é enviada para os servidores públicos.\n\nSeleccione onde a voz é processada:", - "local_description": "Processar a voz nesta máquina", + "content": "O Open Voice OS converte a voz em texto (reconhecimento) e lê as respostas em voz alta (síntese).\n\n - local: esta máquina com servidores públicos de reserva. Descarrega modelos de voz (de centenas de megabytes a alguns gigabytes). Se o reconhecimento local falhar ou não devolver texto, essa gravação é enviada para servidores públicos. Se não for possível configurar o reconhecimento ou a síntese local, essa parte usa servidores públicos; o instalador informa-o.\n - public: os servidores da comunidade processam as suas gravações e as respostas de voz pela internet. Estes servidores de reserva, mantidos por voluntários, não são um serviço de produção: podem ficar lentos ou indisponíveis a qualquer momento.\n\nSeleccione onde a voz é processada:", + "local_description": "Esta máquina com servidores públicos de reserva", "public_description": "Servidores públicos (sei que podem falhar)" }, "summary.sh": { "title": "Open Voice OS Instalação - Resumo", "content": "Quase a terminar. Um breve resumo:\n\n - Implementação: ${METHOD:-}\n - Versão: ${CHANNEL:-}\n - Perfil: ${PROFILE:-}\n - Skills: ${FEATURE_SKILLS_SUMMARY_STATE:-}\n - Skills extra: ${FEATURE_EXTRA_SKILLS_SUMMARY_STATE:-}\n - Home Assistant: ${HOMEASSISTANT_SUMMARY_STATE:-}\n - LLM: ${LLM_SUMMARY_STATE:-}\n - Voz: ${SPEECH_SUMMARY_STATE:-}\n - Otimização: ${TUNING_SUMMARY_STATE:-}\n\nAs decisões tomadas durante o processo de instalação do Open Voice OS foram cuidadosamente consideradas para personalizar o nosso sistema de acordo com as suas necessidades e preferências individuais.\n\nAs definições estão correctas? Se não, seleccione ${BACK_BUTTON:-} para voltar atrás e fazer alterações.", - "speech_local": "nesta máquina", + "speech_local": "nesta máquina com servidores públicos de reserva", "speech_public": "servidores públicos", "speech_public_hardware": "servidores públicos (local: Pi 5 com 8 GB ou melhor)", "speech_public_setup": "servidores públicos (local indisponível aqui)", diff --git a/tui/locales/ca-es/speech.sh b/tui/locales/ca-es/speech.sh index d4f09ba6..cc5c01b6 100644 --- a/tui/locales/ca-es/speech.sh +++ b/tui/locales/ca-es/speech.sh @@ -1,16 +1,14 @@ #!/usr/bin/env bash CONTENT=" -L'Open Voice OS converteix el que dius en text (reconeixement de veu) i llegeix les respostes en veu alta (síntesi de veu). Aquest equip és prou potent per fer totes dues coses ell mateix. +L'Open Voice OS converteix la veu en text (reconeixement) i llegeix les respostes en veu alta (síntesi). -- local: tot s'executa en aquest equip. La teva veu es queda aquí i res no depèn d'internet ni de com d'ocupats estiguin els servidors. L'instal·lador baixa els models de veu del teu idioma, des d'uns quants centenars de megabytes fins a uns quants gigabytes, i el reconeixement manté ocupat el processador mentre parles. -- public: la feina la fan servidors de la comunitat. S'ofereixen de bona voluntat, com a reserva i com a exemple de veu autoallotjada, no com un servei de producció: les respostes triguen més quan molta gent els fa servir alhora, i poden deixar de funcionar en qualsevol moment. No cal baixar res, però les teves gravacions i les respostes viatgen per internet i es processen en aquests servidors. + - local: aquest equip amb servidors públics de reserva. Baixa models de veu (de centenars de megabytes a uns quants gigabytes). Si el reconeixement local falla o no retorna text, aquella gravació s'envia als servidors públics. Si no es pot configurar el reconeixement o la síntesi local, aquella part fa servir servidors públics; l'instal·lador ho indica. + - public: els servidors de la comunitat processen les teves gravacions i les respostes de veu per internet. Aquests servidors de reserva, mantinguts per voluntaris, no són un servei de producció: poden anar lents o deixar de funcionar en qualsevol moment. -Si el reconeixement local falla o no sent res, aquella gravació s'envia als servidors públics. - -Selecciona on es processa la veu, si us plau: +Selecciona on es processa la veu: " TITLE="Instal·lació de l'Open Voice OS - Veu" -LOCAL_DESCRIPTION="Processar la veu en aquest equip" +LOCAL_DESCRIPTION="Aquest equip amb servidors públics de reserva" PUBLIC_DESCRIPTION="Servidors públics (sé que poden caure)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/ca-es/summary.sh b/tui/locales/ca-es/summary.sh index eeb41823..17d00208 100644 --- a/tui/locales/ca-es/summary.sh +++ b/tui/locales/ca-es/summary.sh @@ -23,7 +23,7 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" -SUMMARY_SPEECH_LOCAL="en aquest equip" +SUMMARY_SPEECH_LOCAL="en aquest equip amb servidors públics de reserva" SUMMARY_SPEECH_PUBLIC="servidors públics" SUMMARY_SPEECH_PUBLIC_HARDWARE="servidors públics (local: Pi 5 de 8 GB o més)" SUMMARY_SPEECH_PUBLIC_SETUP="servidors públics (local no disponible aquí)" diff --git a/tui/locales/da/speech.sh b/tui/locales/da/speech.sh index 726b48da..e1b4909b 100644 --- a/tui/locales/da/speech.sh +++ b/tui/locales/da/speech.sh @@ -1,16 +1,14 @@ #!/usr/bin/env bash CONTENT=" -Open Voice OS omsætter det, du siger, til tekst (talegenkendelse) og læser sine svar højt (talesyntese). Denne maskine er kraftig nok til selv at klare begge dele. +Open Voice OS omsætter tale til tekst (genkendelse) og læser svar højt (syntese). -- local: alt kører på denne maskine. Din stemme bliver her, og intet afhænger af internettet eller af, hvor travlt serverne har. Installationsprogrammet henter talemodellerne til dit sprog, fra et par hundrede megabyte til et par gigabyte, og genkendelsen holder processoren beskæftiget, mens du taler. -- public: fællesskabets servere klarer arbejdet. De drives af god vilje, som reserve og som eksempel på selvhostet talebehandling, ikke som en produktionstjeneste: svarene bliver langsommere, når mange bruger dem på samme tid, og serverne kan gå ned når som helst. Der skal ikke hentes noget, men dine optagelser og svarene sendes over internettet og behandles på de servere. + - local: denne maskine med offentlige servere som reserve. Henter talemodeller (hundredvis af megabyte til nogle få gigabyte). Hvis lokal genkendelse fejler eller ikke giver tekst, sendes optagelsen til offentlige servere. Hvis lokal genkendelse eller syntese ikke kan sættes op, bruger den del offentlige servere; installationsprogrammet oplyser det. + - public: fællesskabets servere behandler dine optagelser og talte svar over internettet. Disse frivilligt drevne reserveservere er ikke en produktionstjeneste: de kan blive langsomme eller gå ned når som helst. -Hvis den lokale genkendelse fejler eller ikke hører noget, sendes netop den optagelse til de offentlige servere i stedet. - -Vælg venligst, hvor tale skal behandles: +Vælg, hvor tale skal behandles: " TITLE="Open Voice OS Installation - Tale" -LOCAL_DESCRIPTION="Behandl tale på denne maskine" +LOCAL_DESCRIPTION="Denne maskine med offentlige servere som reserve" PUBLIC_DESCRIPTION="Offentlige servere (jeg ved, de kan gå ned)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/da/summary.sh b/tui/locales/da/summary.sh index a9754ee4..c3e3f904 100644 --- a/tui/locales/da/summary.sh +++ b/tui/locales/da/summary.sh @@ -23,7 +23,7 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" -SUMMARY_SPEECH_LOCAL="på denne maskine" +SUMMARY_SPEECH_LOCAL="på denne maskine med offentlige servere som reserve" SUMMARY_SPEECH_PUBLIC="offentlige servere" SUMMARY_SPEECH_PUBLIC_HARDWARE="offentlige servere (lokal: Pi 5 med 8 GB eller mere)" SUMMARY_SPEECH_PUBLIC_SETUP="offentlige servere (lokal er ikke tilgængelig her)" diff --git a/tui/locales/de-de/speech.sh b/tui/locales/de-de/speech.sh index 10e2210d..1dfd5fc0 100644 --- a/tui/locales/de-de/speech.sh +++ b/tui/locales/de-de/speech.sh @@ -1,16 +1,14 @@ #!/usr/bin/env bash CONTENT=" -Open Voice OS wandelt Gesprochenes in Text um (Spracherkennung) und liest seine Antworten vor (Sprachsynthese). Dieser Rechner ist leistungsfähig genug, beides selbst zu erledigen. +Open Voice OS wandelt Sprache in Text um (Erkennung) und liest Antworten vor (Synthese). -- local: Alles läuft auf diesem Rechner. Ihre Stimme bleibt hier, und nichts hängt vom Internet oder von der Auslastung der Server ab. Der Installer lädt die Sprachmodelle für Ihre Sprache herunter, von einigen hundert Megabyte bis zu ein paar Gigabyte, und die Erkennung lastet den Prozessor aus, während Sie sprechen. -- public: Server der Community übernehmen die Arbeit. Sie werden aus gutem Willen betrieben, als Rückfallebene und als Beispiel für selbst gehostete Sprachverarbeitung, nicht als Produktionsdienst: Antworten dauern länger, wenn viele sie gleichzeitig nutzen, und die Server können jederzeit ausfallen. Es muss nichts heruntergeladen werden, aber Ihre Aufnahmen und die Antworten gehen über das Internet und werden auf diesen Servern verarbeitet. + - local: dieser Rechner mit öffentlichen Servern als Ersatz. Lädt Sprachmodelle herunter (hunderte Megabyte bis einige Gigabyte). Wenn die lokale Erkennung fehlschlägt oder keinen Text liefert, wird die Aufnahme an öffentliche Server gesendet. Lässt sich die lokale Erkennung oder Synthese nicht einrichten, nutzt dieser Teil öffentliche Server; der Installer weist darauf hin. + - public: Community-Server verarbeiten Ihre Aufnahmen und gesprochenen Antworten über das Internet. Diese ehrenamtlich betriebenen Ersatzserver sind kein Produktionsdienst: Sie können jederzeit langsamer werden oder ausfallen. -Wenn die lokale Erkennung fehlschlägt oder nichts hört, wird genau diese Aufnahme stattdessen an die öffentlichen Server geschickt. - -Bitte wählen Sie, wo die Sprache verarbeitet wird: +Bitte wählen Sie, wo Sprache verarbeitet wird: " TITLE="Open Voice OS-Installation - Sprachverarbeitung" -LOCAL_DESCRIPTION="Sprache auf diesem Rechner verarbeiten" +LOCAL_DESCRIPTION="Dieser Rechner mit öffentlichen Servern als Ersatz" PUBLIC_DESCRIPTION="Öffentliche Server (ich weiß, sie können ausfallen)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/de-de/summary.sh b/tui/locales/de-de/summary.sh index 87f7d22b..4c521941 100644 --- a/tui/locales/de-de/summary.sh +++ b/tui/locales/de-de/summary.sh @@ -23,7 +23,7 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" -SUMMARY_SPEECH_LOCAL="auf diesem Rechner" +SUMMARY_SPEECH_LOCAL="auf diesem Rechner mit öffentlichen Servern als Ersatz" SUMMARY_SPEECH_PUBLIC="öffentliche Server" SUMMARY_SPEECH_PUBLIC_HARDWARE="öffentliche Server (lokal: Pi 5 mit 8 GB oder besser)" SUMMARY_SPEECH_PUBLIC_SETUP="öffentliche Server (lokal hier nicht verfügbar)" diff --git a/tui/locales/en-us/speech.sh b/tui/locales/en-us/speech.sh index 47fad248..9e01c18c 100644 --- a/tui/locales/en-us/speech.sh +++ b/tui/locales/en-us/speech.sh @@ -1,16 +1,14 @@ #!/usr/bin/env bash CONTENT=" -Open Voice OS turns what you say into text (speech recognition) and reads its answers out loud (speech synthesis). This machine is powerful enough to do both itself. +Open Voice OS turns speech into text (recognition) and reads replies aloud (synthesis). - - local: everything runs on this machine. Your voice stays here, and nothing depends on the internet or on how busy the servers are. The installer downloads the speech models for your language, from a few hundred megabytes to a few gigabytes, and recognition keeps the processor busy while you talk. - - public: community servers do the work. They run on goodwill, as a backup and as an example of self-hosted speech, not as a production service: answers take longer when many people use them, and the servers can go offline at any time. Nothing to download, but your recordings and the answers travel over the internet and are processed on those servers. - -When local recognition fails or hears nothing, that one recording is sent to the public servers instead. + - local: use this machine with public fallback. Downloads speech models (hundreds of megabytes to a few gigabytes). If local recognition fails or returns no text, that recording is sent to public servers. If local recognition or synthesis cannot be set up, that part uses public servers; the installer reports it. + - public: community servers process your recordings and spoken replies over the internet. These volunteer-run backup servers are not a production service: they can slow down or go offline at any time. Please select where speech is processed: " TITLE="Open Voice OS Installation - Speech" -LOCAL_DESCRIPTION="Run speech on this machine" +LOCAL_DESCRIPTION="This machine with public fallback" PUBLIC_DESCRIPTION="Public servers (I accept they can go offline)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/en-us/summary.sh b/tui/locales/en-us/summary.sh index 3bd5b789..4b9d56de 100644 --- a/tui/locales/en-us/summary.sh +++ b/tui/locales/en-us/summary.sh @@ -23,7 +23,7 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" -SUMMARY_SPEECH_LOCAL="on this machine" +SUMMARY_SPEECH_LOCAL="on this machine with public fallback" SUMMARY_SPEECH_PUBLIC="public servers" SUMMARY_SPEECH_PUBLIC_HARDWARE="public servers (local needs a Pi 5 with 8 GB or better)" SUMMARY_SPEECH_PUBLIC_SETUP="public servers (local is not available here)" diff --git a/tui/locales/es-es/speech.sh b/tui/locales/es-es/speech.sh index cf7ddfb2..79f23de3 100644 --- a/tui/locales/es-es/speech.sh +++ b/tui/locales/es-es/speech.sh @@ -1,16 +1,14 @@ #!/usr/bin/env bash CONTENT=" -Open Voice OS convierte lo que dice en texto (reconocimiento de voz) y lee sus respuestas en voz alta (síntesis de voz). Este equipo es lo bastante potente para hacer ambas cosas por sí mismo. +Open Voice OS convierte la voz en texto (reconocimiento) y lee las respuestas en voz alta (síntesis). -- local: todo se ejecuta en este equipo. Su voz se queda aquí y nada depende de internet ni de lo ocupados que estén los servidores. El instalador descarga los modelos de voz de su idioma, desde unos cientos de megabytes hasta unos pocos gigabytes, y el reconocimiento mantiene ocupado el procesador mientras habla. -- public: el trabajo lo hacen servidores de la comunidad. Se ofrecen de buena voluntad, como respaldo y como ejemplo de voz autoalojada, no como un servicio de producción: las respuestas tardan más cuando mucha gente los usa a la vez, y pueden dejar de funcionar en cualquier momento. No hay nada que descargar, pero sus grabaciones y las respuestas viajan por internet y se procesan en esos servidores. - -Si el reconocimiento local falla o no oye nada, esa grabación se envía a los servidores públicos. + - local: este equipo con servidores públicos de respaldo. Descarga modelos de voz (de cientos de megabytes a unos pocos gigabytes). Si el reconocimiento local falla o no devuelve texto, esa grabación se envía a servidores públicos. Si no se puede configurar el reconocimiento o la síntesis local, esa parte usa servidores públicos; el instalador lo indica. + - public: los servidores de la comunidad procesan sus grabaciones y las respuestas de voz por internet. Estos servidores de respaldo, mantenidos por voluntarios, no son un servicio de producción: pueden ralentizarse o dejar de funcionar en cualquier momento. Seleccione dónde se procesa la voz: " TITLE="Instalación de Open Voice OS - Voz" -LOCAL_DESCRIPTION="Procesar la voz en este equipo" +LOCAL_DESCRIPTION="Este equipo con servidores públicos de respaldo" PUBLIC_DESCRIPTION="Servidores públicos (sé que pueden caerse)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/es-es/summary.sh b/tui/locales/es-es/summary.sh index 18dadf7d..91c6cc7f 100644 --- a/tui/locales/es-es/summary.sh +++ b/tui/locales/es-es/summary.sh @@ -23,7 +23,7 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" -SUMMARY_SPEECH_LOCAL="en este equipo" +SUMMARY_SPEECH_LOCAL="en este equipo con servidores públicos de respaldo" SUMMARY_SPEECH_PUBLIC="servidores públicos" SUMMARY_SPEECH_PUBLIC_HARDWARE="servidores públicos (local: Pi 5 con 8 GB o más)" SUMMARY_SPEECH_PUBLIC_SETUP="servidores públicos (local no disponible aquí)" diff --git a/tui/locales/eu-es/speech.sh b/tui/locales/eu-es/speech.sh index 449d2cb3..cab46128 100644 --- a/tui/locales/eu-es/speech.sh +++ b/tui/locales/eu-es/speech.sh @@ -1,16 +1,14 @@ #!/usr/bin/env bash CONTENT=" -Open Voice OS-k esaten duzuna testu bihurtzen du (ahots-ezagutza) eta bere erantzunak ozen irakurtzen ditu (ahots-sintesia). Makina honek bi lanak berak egiteko adina indar du. +Open Voice OS-k ahotsa testu bihurtzen du (ezagutza) eta erantzunak ozen irakurtzen ditu (sintesia). - - local: dena makina honetan exekutatzen da. Zure ahotsa hemen geratzen da, eta ezer ez dago Interneten edo zerbitzarien lan-kargaren mende. Instalatzaileak zure hizkuntzako ahots-ereduak deskargatzen ditu, ehunka megabytetik gigabyte gutxi batzuetara, eta ezagutzak prozesadorea lanpetuta mantentzen du hitz egiten duzun bitartean. - - public: komunitatearen zerbitzariek egiten dute lana. Borondate onez eskaintzen dira, babes gisa eta norberak ostatatutako ahotsaren adibide gisa, ez produkzio-zerbitzu gisa: erantzunak motelago iristen dira jende askok aldi berean erabiltzen dituenean, eta edozein unetan gelditu daitezke. Ez dago ezer deskargatu beharrik, baina zure grabazioak eta erantzunak Internet bidez doaz eta zerbitzari horietan prozesatzen dira. - -Ezagutza lokalak huts egiten badu edo ezer entzuten ez badu, grabazio hori zerbitzari publikoetara bidaltzen da. + - local: makina hau, zerbitzari publikoak ordezko gisa erabiliz. Ahots-ereduak deskargatzen ditu (ehunka megabytetik gigabyte gutxi batzuetara). Ezagutza lokalak huts egiten badu edo testurik itzultzen ez badu, grabazioa zerbitzari publikoetara bidaltzen da. Ezagutza edo sintesi lokala ezin bada konfiguratu, zati horrek zerbitzari publikoak erabiltzen ditu; instalatzaileak horren berri ematen du. + - public: komunitatearen zerbitzariek zure grabazioak eta ahots-erantzunak Internet bidez prozesatzen dituzte. Boluntarioek mantendutako ordezko zerbitzari hauek ez dira produkzio-zerbitzu bat: edozein unetan moteldu edo gelditu daitezke. Aukeratu non prozesatzen den ahotsa: " TITLE="Open Voice OS instalazioa - Ahotsa" -LOCAL_DESCRIPTION="Prozesatu ahotsa makina honetan" +LOCAL_DESCRIPTION="Makina hau, zerbitzari publikoak ordezko gisa" PUBLIC_DESCRIPTION="Zerbitzari publikoak (badakit eror daitezkeela)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/eu-es/summary.sh b/tui/locales/eu-es/summary.sh index eb616ae8..158f1759 100644 --- a/tui/locales/eu-es/summary.sh +++ b/tui/locales/eu-es/summary.sh @@ -23,7 +23,7 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" -SUMMARY_SPEECH_LOCAL="makina honetan" +SUMMARY_SPEECH_LOCAL="makina honetan, zerbitzari publikoak ordezko gisa" SUMMARY_SPEECH_PUBLIC="zerbitzari publikoak" SUMMARY_SPEECH_PUBLIC_HARDWARE="zerbitzari publikoak (lokala: 8 GB-ko Pi 5 edo hobea)" SUMMARY_SPEECH_PUBLIC_SETUP="zerbitzari publikoak (lokala ez dago hemen)" diff --git a/tui/locales/fr-fr/speech.sh b/tui/locales/fr-fr/speech.sh index 66d2ec86..0ee2edf8 100644 --- a/tui/locales/fr-fr/speech.sh +++ b/tui/locales/fr-fr/speech.sh @@ -1,16 +1,14 @@ #!/usr/bin/env bash CONTENT=" -Open Voice OS transforme ce que vous dites en texte (reconnaissance vocale) et lit ses réponses à voix haute (synthèse vocale). Cette machine est assez puissante pour faire les deux elle-même. +Open Voice OS transforme la parole en texte (reconnaissance) et lit les réponses à voix haute (synthèse). -- local : tout fonctionne sur cette machine. Votre voix reste ici, et rien ne dépend d'Internet ni de la charge des serveurs. L'installateur télécharge les modèles vocaux de votre langue, de quelques centaines de mégaoctets à quelques gigaoctets, et la reconnaissance occupe le processeur pendant que vous parlez. -- public : des serveurs communautaires font le travail. Ils sont fournis de bonne volonté, comme solution de secours et comme exemple de traitement de la parole auto-hébergé, pas comme un service de production : les réponses ralentissent quand beaucoup de monde les utilise, et ils peuvent s'arrêter à tout moment. Rien à télécharger, mais vos enregistrements et les réponses passent par Internet et sont traités sur ces serveurs. - -Si la reconnaissance locale échoue ou n'entend rien, cet enregistrement-là est envoyé aux serveurs publics à la place. + - local : cette machine avec des serveurs publics en secours. Télécharge des modèles vocaux (de quelques centaines de mégaoctets à quelques gigaoctets). Si la reconnaissance locale échoue ou ne renvoie aucun texte, cet enregistrement est envoyé aux serveurs publics. Si la reconnaissance ou la synthèse locale ne peut pas être configurée, cette partie utilise les serveurs publics ; l'installateur le signale. + - public : les serveurs communautaires traitent vos enregistrements et les réponses vocales via Internet. Ces serveurs de secours, gérés par des bénévoles, ne sont pas un service de production : ils peuvent ralentir ou s'arrêter à tout moment. Veuillez sélectionner où la parole est traitée : " TITLE="Open Voice OS Installation - Traitement de la parole" -LOCAL_DESCRIPTION="Traiter la parole sur cette machine" +LOCAL_DESCRIPTION="Cette machine avec des serveurs publics en secours" PUBLIC_DESCRIPTION="Serveurs publics (je sais qu'ils peuvent tomber)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/fr-fr/summary.sh b/tui/locales/fr-fr/summary.sh index 34e8bacd..d924e619 100644 --- a/tui/locales/fr-fr/summary.sh +++ b/tui/locales/fr-fr/summary.sh @@ -23,7 +23,7 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" -SUMMARY_SPEECH_LOCAL="sur cette machine" +SUMMARY_SPEECH_LOCAL="sur cette machine avec des serveurs publics en secours" SUMMARY_SPEECH_PUBLIC="serveurs publics" SUMMARY_SPEECH_PUBLIC_HARDWARE="serveurs publics (local : Pi 5 8 Go minimum)" SUMMARY_SPEECH_PUBLIC_SETUP="serveurs publics (local indisponible ici)" diff --git a/tui/locales/gl-es/speech.sh b/tui/locales/gl-es/speech.sh index dcfc185b..e32855b5 100644 --- a/tui/locales/gl-es/speech.sh +++ b/tui/locales/gl-es/speech.sh @@ -1,16 +1,14 @@ #!/usr/bin/env bash CONTENT=" -Open Voice OS converte o que dis en texto (recoñecemento de voz) e le as súas respostas en voz alta (síntese de voz). Este equipo é abondo potente para facer as dúas cousas el mesmo. +Open Voice OS converte a voz en texto (recoñecemento) e le as respostas en voz alta (síntese). - - local: todo se executa neste equipo. A túa voz queda aquí e nada depende de internet nin de canto de ocupados estean os servidores. O instalador descarga os modelos de voz do teu idioma, desde uns centos de megabytes ata uns poucos gigabytes, e o recoñecemento mantén ocupado o procesador mentres falas. - - public: o traballo fano servidores da comunidade. Ofrécense de boa vontade, como respaldo e como exemplo de voz autoaloxada, non como un servizo de produción: as respostas tardan máis cando moita xente os usa á vez, e poden deixar de funcionar en calquera momento. Non hai nada que descargar, pero as túas gravacións e as respostas viaxan por internet e procésanse neses servidores. - -Se o recoñecemento local falla ou non oe nada, esa gravación envíase aos servidores públicos. + - local: este equipo con servidores públicos de respaldo. Descarga modelos de voz (de centos de megabytes a uns poucos gigabytes). Se o recoñecemento local falla ou non devolve texto, esa gravación envíase a servidores públicos. Se non se pode configurar o recoñecemento ou a síntese local, esa parte usa servidores públicos; o instalador indícao. + - public: os servidores da comunidade procesan as túas gravacións e as respostas de voz por internet. Estes servidores de respaldo, mantidos por voluntarios, non son un servizo de produción: poden ir máis lentos ou deixar de funcionar en calquera momento. Selecciona onde se procesa a voz: " TITLE="Instalación de Open Voice OS - Voz" -LOCAL_DESCRIPTION="Procesar a voz neste equipo" +LOCAL_DESCRIPTION="Este equipo con servidores públicos de respaldo" PUBLIC_DESCRIPTION="Servidores públicos (sei que poden caer)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/gl-es/summary.sh b/tui/locales/gl-es/summary.sh index f520b69a..06a6e85f 100644 --- a/tui/locales/gl-es/summary.sh +++ b/tui/locales/gl-es/summary.sh @@ -23,7 +23,7 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" -SUMMARY_SPEECH_LOCAL="neste equipo" +SUMMARY_SPEECH_LOCAL="neste equipo con servidores públicos de respaldo" SUMMARY_SPEECH_PUBLIC="servidores públicos" SUMMARY_SPEECH_PUBLIC_HARDWARE="servidores públicos (local: Pi 5 de 8 GB ou mellor)" SUMMARY_SPEECH_PUBLIC_SETUP="servidores públicos (local non dispoñible aquí)" diff --git a/tui/locales/hi-in/speech.sh b/tui/locales/hi-in/speech.sh index 746e74ec..3c0eb459 100644 --- a/tui/locales/hi-in/speech.sh +++ b/tui/locales/hi-in/speech.sh @@ -1,16 +1,14 @@ #!/usr/bin/env bash CONTENT=" -Open Voice OS आपकी कही बात को टेक्स्ट में बदलता है (वाक् पहचान) और अपने जवाब बोलकर सुनाता है (वाक् संश्लेषण)। यह मशीन ये दोनों काम ख़ुद करने के लिए काफ़ी शक्तिशाली है। +Open Voice OS आवाज़ को टेक्स्ट में बदलता है (वाक् पहचान) और जवाब बोलकर सुनाता है (वाक् संश्लेषण)। - - local: सब कुछ इसी मशीन पर चलता है। आपकी आवाज़ यहीं रहती है, और कुछ भी इंटरनेट या सर्वरों की व्यस्तता पर निर्भर नहीं करता। इंस्टॉलर आपकी भाषा के वाक् मॉडल डाउनलोड करता है, जो कुछ सौ मेगाबाइट से लेकर कुछ गीगाबाइट तक के होते हैं, और आपके बोलते समय पहचान प्रोसेसर को व्यस्त रखती है। - - public: समुदाय के सर्वर यह काम करते हैं। इन्हें सद्भावना से चलाया जाता है, बैकअप के तौर पर और सेल्फ़-होस्टेड वाक् प्रसंस्करण के उदाहरण के रूप में, किसी प्रोडक्शन सेवा की तरह नहीं: जब बहुत से लोग एक साथ इनका उपयोग करते हैं तो जवाब धीमे हो जाते हैं, और ये सर्वर कभी भी बंद हो सकते हैं। कुछ डाउनलोड नहीं करना पड़ता, लेकिन आपकी रिकॉर्डिंग और जवाब इंटरनेट से होकर इन्हीं सर्वरों पर प्रोसेस होते हैं। - -अगर लोकल पहचान विफल हो जाए या कुछ न सुन पाए, तो वह रिकॉर्डिंग सार्वजनिक सर्वरों पर भेज दी जाती है। + - local: यह मशीन, ज़रूरत पड़ने पर सार्वजनिक सर्वरों की मदद से। वाक् मॉडल डाउनलोड होते हैं (कुछ सौ मेगाबाइट से कुछ गीगाबाइट तक)। अगर स्थानीय पहचान विफल हो या कोई टेक्स्ट न लौटाए, तो वह रिकॉर्डिंग सार्वजनिक सर्वरों पर भेजी जाती है। अगर स्थानीय पहचान या संश्लेषण स्थापित नहीं हो पाता, तो उस हिस्से के लिए सार्वजनिक सर्वर इस्तेमाल होते हैं; इंस्टॉलर इसकी जानकारी देता है। + - public: समुदाय के सर्वर आपकी रिकॉर्डिंग और बोले जाने वाले जवाब इंटरनेट पर प्रोसेस करते हैं। स्वयंसेवकों द्वारा चलाए जा रहे ये वैकल्पिक सर्वर नियमित सेवा नहीं हैं: ये धीमे पड़ सकते हैं या कभी भी बंद हो सकते हैं। कृपया चुनें कि आवाज़ कहाँ प्रोसेस हो: " TITLE="Open Voice OS Installation - आवाज़" -LOCAL_DESCRIPTION="इसी मशीन पर आवाज़ प्रोसेस करें" +LOCAL_DESCRIPTION="यह मशीन, ज़रूरत पर सार्वजनिक सर्वरों की मदद से" PUBLIC_DESCRIPTION="सार्वजनिक सर्वर (ये कभी भी बंद हो सकते हैं)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/hi-in/summary.sh b/tui/locales/hi-in/summary.sh index c3be8234..328911aa 100644 --- a/tui/locales/hi-in/summary.sh +++ b/tui/locales/hi-in/summary.sh @@ -21,7 +21,7 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" -SUMMARY_SPEECH_LOCAL="इसी मशीन पर" +SUMMARY_SPEECH_LOCAL="इस मशीन पर, ज़रूरत पर सार्वजनिक सर्वरों की मदद से" SUMMARY_SPEECH_PUBLIC="सार्वजनिक सर्वर" SUMMARY_SPEECH_PUBLIC_HARDWARE="सार्वजनिक सर्वर (लोकल: 8 GB वाला Pi 5 या बेहतर)" SUMMARY_SPEECH_PUBLIC_SETUP="सार्वजनिक सर्वर (यहाँ लोकल उपलब्ध नहीं)" diff --git a/tui/locales/it-it/speech.sh b/tui/locales/it-it/speech.sh index 94f86b38..b9254a50 100644 --- a/tui/locales/it-it/speech.sh +++ b/tui/locales/it-it/speech.sh @@ -1,16 +1,14 @@ #!/usr/bin/env bash CONTENT=" -Open Voice OS trasforma ciò che dici in testo (riconoscimento vocale) e legge le risposte ad alta voce (sintesi vocale). Questa macchina è abbastanza potente da fare entrambe le cose da sola. +Open Voice OS trasforma la voce in testo (riconoscimento) e legge le risposte ad alta voce (sintesi). - - local: tutto gira su questa macchina. La tua voce resta qui e nulla dipende da internet o da quanto sono occupati i server. L'installer scarica i modelli vocali per la tua lingua, da qualche centinaio di megabyte a qualche gigabyte, e il riconoscimento tiene occupato il processore mentre parli. - - public: il lavoro lo fanno server della comunità. Sono offerti per buona volontà, come riserva e come esempio di voce in self-hosting, non come servizio di produzione: le risposte rallentano quando molte persone li usano contemporaneamente, e possono andare offline in qualsiasi momento. Niente da scaricare, ma le tue registrazioni e le risposte viaggiano su internet e vengono elaborate su quei server. - -Se il riconoscimento locale fallisce o non sente nulla, quella registrazione viene inviata ai server pubblici. + - local: questa macchina con server pubblici di riserva. Scarica modelli vocali (da centinaia di megabyte a pochi gigabyte). Se il riconoscimento locale fallisce o non restituisce testo, quella registrazione viene inviata ai server pubblici. Se non è possibile configurare il riconoscimento o la sintesi locale, quella parte usa i server pubblici; l'installer lo segnala. + - public: i server della comunità elaborano le tue registrazioni e le risposte vocali tramite internet. Questi server di riserva, gestiti da volontari, non sono un servizio di produzione: possono rallentare o andare offline in qualsiasi momento. Seleziona dove elaborare la voce: " TITLE="Installazione di Open Voice OS - Voce" -LOCAL_DESCRIPTION="Elaborare la voce su questa macchina" +LOCAL_DESCRIPTION="Questa macchina con server pubblici di riserva" PUBLIC_DESCRIPTION="Server pubblici (so che possono andare offline)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/it-it/summary.sh b/tui/locales/it-it/summary.sh index 31ef4b60..63d752e9 100644 --- a/tui/locales/it-it/summary.sh +++ b/tui/locales/it-it/summary.sh @@ -23,7 +23,7 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" -SUMMARY_SPEECH_LOCAL="su questa macchina" +SUMMARY_SPEECH_LOCAL="su questa macchina con server pubblici di riserva" SUMMARY_SPEECH_PUBLIC="server pubblici" SUMMARY_SPEECH_PUBLIC_HARDWARE="server pubblici (locale: Pi 5 con 8 GB o superiore)" SUMMARY_SPEECH_PUBLIC_SETUP="server pubblici (locale non disponibile qui)" diff --git a/tui/locales/kab-dz/speech.sh b/tui/locales/kab-dz/speech.sh index 081feae1..bbed9f9d 100644 --- a/tui/locales/kab-dz/speech.sh +++ b/tui/locales/kab-dz/speech.sh @@ -1,16 +1,14 @@ #!/usr/bin/env bash CONTENT=" -Open Voice OS yettbeddil ayen i d-tenniḍ ɣer uḍris (aɛqal n taɣect) yerna yeqqar-d tiririyin-is s taɣect (asuddes n taɣect). Tamacint-a tezmer ad txeddem snat-a s yiman-is. +Open Voice OS yettbeddil taɣect ɣer uḍris (aɛqal) yerna yeqqar-d tiririyin s taɣect (asuddes). - - local: kullec ad iteddu ɣef tmacint-a. Taɣect-ik ad teqqim da, ulac ayen icudden ɣer Internet neɣ ɣer yiqeddacen. Amesbeddi ad d-yessader timudmin n taɣect n tutlayt-ik, seg kra n yimiḍ n MB alamma d kra n GB, yerna aɛqal ad yesseqdec amesbadu mi ara tettmeslayeḍ. - - public: d iqeddacen n temɣiwant ara ixedmen axeddim. Ttwafken-d s lebɣi, am tarrayt n uɛiwen d umedya n taɣect i yesbedden yiman-is, maci am umeẓlu n tfalit: tiririyin ad ẓẓayent mi ara ten-sqedcen aṭas n medden ɣef tikkelt, yerna zemren ad ḥbesen melmi tebɣu tili. Ulac ayen ara d-tessadreḍ, maca iseklasen-ik d tririyin ad ɛeddin s Internet u ad ttwasenfaren ɣef yiqeddacen-a. - -Ma yecceḍ uɛqal adigan neɣ ur yesla i kra, asekles-nni ad yettwazen ɣer yiqeddacen izayazen. + - local: tamacint-a s yiqeddacen izayazen d tallalt. Ad d-yessader timudmin n taɣect (seg yimiḍ n MB alamma d kra n GB). Ma yecceḍ uɛqal adigan neɣ ur d-yerri ara aḍris, asekles-nni ad yettwazen ɣer yiqeddacen izayazen. Ma ur yezmir ara ad yesbedd aɛqal neɣ asuddes adigan, aḥric-nni ad yesseqdec iqeddacen izayazen; amesbeddi ad d-yefk talɣut ɣef waya. + - public: iqeddacen n temɣiwant senfaren iseklasen-ik d tririyin n taɣect s Internet. Iqeddacen-a n tallalt ttwasefraken s lebɣi, mačči d ameẓlu n tfalit: zemren ad ẓẓayen neɣ ad ḥbesen melmi tebɣu tili. Ttxil-k fren anida ara tettwasenfar taɣect: " TITLE="Asbeddi n Open Voice OS - Taɣect" -LOCAL_DESCRIPTION="Taɣect ɣef tmacint-a" +LOCAL_DESCRIPTION="Tamacint-a s yiqeddacen izayazen d tallalt" PUBLIC_DESCRIPTION="Iqeddacen izayazen (ẓriɣ zemren ad ḥbesen)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/kab-dz/summary.sh b/tui/locales/kab-dz/summary.sh index c5390e3a..9d98fedc 100644 --- a/tui/locales/kab-dz/summary.sh +++ b/tui/locales/kab-dz/summary.sh @@ -23,7 +23,7 @@ SUMMARY_STATE_DISABLED="d arurmid" SUMMARY_STATE_UNSUPPORTED_PROFILE="yettawfran (ur yettwasefrek ara deg umaɣnu-a)" SUMMARY_STATE_MISSING_URL="yettwafran (txuṣṣ URL; as yettwazgal)" SUMMARY_STATE_MISSING_CONFIGURATION="yettwafran (txuṣṣ tawila; as yettwazgal)" -SUMMARY_SPEECH_LOCAL="ɣef tmacint-a" +SUMMARY_SPEECH_LOCAL="ɣef tmacint-a s yiqeddacen izayazen d tallalt" SUMMARY_SPEECH_PUBLIC="iqeddacen izayazen" SUMMARY_SPEECH_PUBLIC_HARDWARE="iqeddacen izayazen (adigan: Pi 5 s 8 GB neɣ ugar)" SUMMARY_SPEECH_PUBLIC_SETUP="iqeddacen izayazen (adigan ulac-it dagi)" diff --git a/tui/locales/nl-nl/speech.sh b/tui/locales/nl-nl/speech.sh index fa6d73fd..7f54b86b 100644 --- a/tui/locales/nl-nl/speech.sh +++ b/tui/locales/nl-nl/speech.sh @@ -1,16 +1,14 @@ #!/usr/bin/env bash CONTENT=" -Open Voice OS zet wat je zegt om in tekst (spraakherkenning) en leest zijn antwoorden voor (spraaksynthese). Deze machine is krachtig genoeg om beide zelf te doen. +Open Voice OS zet spraak om in tekst (herkenning) en leest antwoorden voor (synthese). -- local: alles draait op deze machine. Je stem blijft hier, en niets hangt af van internet of van hoe druk de servers zijn. Het installatieprogramma downloadt de spraakmodellen voor je taal, van een paar honderd megabyte tot een paar gigabyte, en de herkenning houdt de processor bezig terwijl je praat. -- public: servers van de community doen het werk. Ze draaien uit goede wil, als reserve en als voorbeeld van zelf gehoste spraakverwerking, niet als productiedienst: antwoorden duren langer wanneer veel mensen ze tegelijk gebruiken, en de servers kunnen op elk moment uitvallen. Je hoeft niets te downloaden, maar je opnames en de antwoorden gaan via internet en worden op die servers verwerkt. - -Als de lokale herkenning mislukt of niets hoort, wordt die ene opname naar de openbare servers gestuurd. + - local: deze machine met openbare servers als reserve. Downloadt spraakmodellen (honderden megabytes tot enkele gigabytes). Als lokale herkenning mislukt of geen tekst oplevert, gaat die opname naar openbare servers. Als lokale herkenning of synthese niet kan worden ingesteld, gebruikt dat onderdeel openbare servers; het installatieprogramma meldt dit. + - public: servers van de community verwerken je opnames en gesproken antwoorden via internet. Deze reserveservers draaien dankzij vrijwilligers en zijn geen productiedienst: ze kunnen op elk moment traag worden of uitvallen. Selecteer waar spraak wordt verwerkt: " TITLE="Open Voice OS Installatie - Spraak" -LOCAL_DESCRIPTION="Spraak op deze machine verwerken" +LOCAL_DESCRIPTION="Deze machine met openbare servers als reserve" PUBLIC_DESCRIPTION="Openbare servers (ik weet dat ze kunnen uitvallen)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/nl-nl/summary.sh b/tui/locales/nl-nl/summary.sh index d3515e2b..4bc3456b 100644 --- a/tui/locales/nl-nl/summary.sh +++ b/tui/locales/nl-nl/summary.sh @@ -23,7 +23,7 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" -SUMMARY_SPEECH_LOCAL="op deze machine" +SUMMARY_SPEECH_LOCAL="op deze machine met openbare servers als reserve" SUMMARY_SPEECH_PUBLIC="openbare servers" SUMMARY_SPEECH_PUBLIC_HARDWARE="openbare servers (lokaal: Pi 5 met 8 GB of beter)" SUMMARY_SPEECH_PUBLIC_SETUP="openbare servers (lokaal hier niet beschikbaar)" diff --git a/tui/locales/pl-pl/speech.sh b/tui/locales/pl-pl/speech.sh index 63217eca..c8f6b033 100644 --- a/tui/locales/pl-pl/speech.sh +++ b/tui/locales/pl-pl/speech.sh @@ -1,16 +1,14 @@ #!/usr/bin/env bash CONTENT=" -Open Voice OS zamienia to, co mówisz, na tekst (rozpoznawanie mowy) i czyta swoje odpowiedzi na głos (synteza mowy). Ten komputer jest na tyle wydajny, żeby robić jedno i drugie samodzielnie. +Open Voice OS zamienia mowę na tekst (rozpoznawanie) i czyta odpowiedzi na głos (synteza). -- local: wszystko działa na tym komputerze. Twój głos zostaje tutaj i nic nie zależy od internetu ani od obciążenia serwerów. Instalator pobiera modele mowy dla Twojego języka, od kilkuset megabajtów do kilku gigabajtów, a rozpoznawanie obciąża procesor, gdy mówisz. -- public: pracę wykonują serwery społeczności. Działają w dobrej wierze, jako zapas i przykład samodzielnie hostowanego przetwarzania mowy, a nie jako usługa produkcyjna: odpowiedzi zwalniają, gdy korzysta z nich wiele osób naraz, a serwery w każdej chwili mogą przestać działać. Nic nie trzeba pobierać, ale Twoje nagrania i odpowiedzi trafiają przez internet na te serwery i tam są przetwarzane. - -Jeśli lokalne rozpoznawanie zawiedzie lub nic nie usłyszy, to nagranie zostanie wysłane do serwerów publicznych. + - local: ten komputer z serwerami publicznymi w rezerwie. Pobiera modele mowy (od setek megabajtów do kilku gigabajtów). Jeśli lokalne rozpoznawanie zawiedzie lub nie zwróci tekstu, nagranie jest wysyłane do serwerów publicznych. Jeśli nie da się skonfigurować lokalnego rozpoznawania lub syntezy, ta część używa serwerów publicznych; instalator o tym informuje. + - public: serwery społeczności przetwarzają Twoje nagrania i odpowiedzi głosowe przez internet. Te zapasowe serwery prowadzone przez wolontariuszy nie są usługą produkcyjną: mogą zwolnić lub przestać działać w każdej chwili. Wybierz, gdzie ma być przetwarzana mowa: " TITLE="Instalacja Open Voice OS - Mowa" -LOCAL_DESCRIPTION="Przetwarzaj mowę na tym komputerze" +LOCAL_DESCRIPTION="Ten komputer z serwerami publicznymi w rezerwie" PUBLIC_DESCRIPTION="Serwery publiczne (wiem, że mogą przestać działać)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/pl-pl/summary.sh b/tui/locales/pl-pl/summary.sh index 6685fbad..30d2b001 100644 --- a/tui/locales/pl-pl/summary.sh +++ b/tui/locales/pl-pl/summary.sh @@ -23,7 +23,7 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" -SUMMARY_SPEECH_LOCAL="na tym komputerze" +SUMMARY_SPEECH_LOCAL="na tym komputerze z serwerami publicznymi w rezerwie" SUMMARY_SPEECH_PUBLIC="serwery publiczne" SUMMARY_SPEECH_PUBLIC_HARDWARE="serwery publiczne (lokalnie: Pi 5 z 8 GB lub lepszy)" SUMMARY_SPEECH_PUBLIC_SETUP="serwery publiczne (tryb lokalny tu niedostępny)" diff --git a/tui/locales/pt-pt/speech.sh b/tui/locales/pt-pt/speech.sh index f4fe6c30..523ee691 100644 --- a/tui/locales/pt-pt/speech.sh +++ b/tui/locales/pt-pt/speech.sh @@ -1,16 +1,14 @@ #!/usr/bin/env bash CONTENT=" -O Open Voice OS converte o que diz em texto (reconhecimento de voz) e lê as respostas em voz alta (síntese de voz). Esta máquina é suficientemente potente para fazer as duas coisas sozinha. +O Open Voice OS converte a voz em texto (reconhecimento) e lê as respostas em voz alta (síntese). - - local: tudo corre nesta máquina. A sua voz fica aqui e nada depende da internet nem da ocupação dos servidores. O instalador descarrega os modelos de voz do seu idioma, de algumas centenas de megabytes a alguns gigabytes, e o reconhecimento mantém o processador ocupado enquanto fala. - - public: o trabalho é feito por servidores da comunidade. São disponibilizados por boa vontade, como reserva e como exemplo de voz auto-alojada, não como um serviço de produção: as respostas demoram mais quando muitas pessoas os usam ao mesmo tempo, e podem ficar indisponíveis a qualquer momento. Não há nada para descarregar, mas as suas gravações e as respostas viajam pela internet e são processadas nesses servidores. - -Se o reconhecimento local falhar ou não ouvir nada, essa gravação é enviada para os servidores públicos. + - local: esta máquina com servidores públicos de reserva. Descarrega modelos de voz (de centenas de megabytes a alguns gigabytes). Se o reconhecimento local falhar ou não devolver texto, essa gravação é enviada para servidores públicos. Se não for possível configurar o reconhecimento ou a síntese local, essa parte usa servidores públicos; o instalador informa-o. + - public: os servidores da comunidade processam as suas gravações e as respostas de voz pela internet. Estes servidores de reserva, mantidos por voluntários, não são um serviço de produção: podem ficar lentos ou indisponíveis a qualquer momento. Seleccione onde a voz é processada: " TITLE="Open Voice OS Instalação - Voz" -LOCAL_DESCRIPTION="Processar a voz nesta máquina" +LOCAL_DESCRIPTION="Esta máquina com servidores públicos de reserva" PUBLIC_DESCRIPTION="Servidores públicos (sei que podem falhar)" export CONTENT TITLE LOCAL_DESCRIPTION PUBLIC_DESCRIPTION diff --git a/tui/locales/pt-pt/summary.sh b/tui/locales/pt-pt/summary.sh index 990df843..dfec05bd 100644 --- a/tui/locales/pt-pt/summary.sh +++ b/tui/locales/pt-pt/summary.sh @@ -23,7 +23,7 @@ SUMMARY_STATE_DISABLED="disabled" SUMMARY_STATE_UNSUPPORTED_PROFILE="selected (not supported for this profile)" SUMMARY_STATE_MISSING_URL="selected (missing URL; will be skipped)" SUMMARY_STATE_MISSING_CONFIGURATION="selected (missing configuration; will be skipped)" -SUMMARY_SPEECH_LOCAL="nesta máquina" +SUMMARY_SPEECH_LOCAL="nesta máquina com servidores públicos de reserva" SUMMARY_SPEECH_PUBLIC="servidores públicos" SUMMARY_SPEECH_PUBLIC_HARDWARE="servidores públicos (local: Pi 5 com 8 GB ou melhor)" SUMMARY_SPEECH_PUBLIC_SETUP="servidores públicos (local indisponível aqui)" diff --git a/tui/summary.sh b/tui/summary.sh index c145f960..dacb3a26 100644 --- a/tui/summary.sh +++ b/tui/summary.sh @@ -50,7 +50,7 @@ function summary_speech_state() { printf '%s\n' "${SUMMARY_SPEECH_UNUSED:-not used by this profile}" elif local_speech_available; then if [ "${SPEECH_ENGINE:-}" == "local" ]; then - printf '%s\n' "${SUMMARY_SPEECH_LOCAL:-on this machine}" + printf '%s\n' "${SUMMARY_SPEECH_LOCAL:-on this machine with public fallback}" else printf '%s\n' "${SUMMARY_SPEECH_PUBLIC:-public servers}" fi