mirror of
https://github.com/Quad4-Software/MeshChatX.git
synced 2026-08-18 09:49:09 -04:00
feat: update dialog confirmation handling across components and improve accessibility with ARIA attributes
This commit is contained in:
parent
ae7eecf82d
commit
5c257a101b
32 changed files with 696 additions and 162 deletions
BIN
meshchatx.rsm
BIN
meshchatx.rsm
Binary file not shown.
|
|
@ -2,12 +2,22 @@
|
|||
|
||||
<template>
|
||||
<Transition name="confirm-dialog">
|
||||
<div v-if="pendingConfirm" class="fixed inset-0 z-9999 flex items-center justify-center p-4">
|
||||
<div
|
||||
v-if="pendingConfirm"
|
||||
class="fixed inset-0 z-9999 flex items-center justify-center p-4"
|
||||
role="alertdialog"
|
||||
aria-modal="true"
|
||||
:aria-labelledby="titleId"
|
||||
:aria-describedby="messageId"
|
||||
>
|
||||
<div class="fixed inset-0 bg-black/50 backdrop-blur-xs shadow-2xl" @click="cancel"></div>
|
||||
|
||||
<div
|
||||
ref="dialogPanel"
|
||||
class="relative w-full sm:w-auto sm:min-w-[400px] sm:max-w-md bg-white dark:bg-zinc-900 sm:rounded-3xl rounded-3xl shadow-2xl border border-gray-200 dark:border-zinc-800 overflow-hidden transform transition-all"
|
||||
tabindex="-1"
|
||||
@click.stop
|
||||
@keydown.esc.prevent="cancel"
|
||||
>
|
||||
<div class="p-8">
|
||||
<div class="flex items-start mb-6">
|
||||
|
|
@ -17,10 +27,13 @@
|
|||
<MaterialDesignIcon icon-name="alert-circle" class="w-6 h-6" />
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="text-xl font-black text-gray-900 dark:text-white mb-2">
|
||||
{{ $t("common.confirm_action") }}
|
||||
<h3 :id="titleId" class="text-xl font-black text-gray-900 dark:text-white mb-2">
|
||||
{{ pendingConfirm.title || $t("common.confirm_action") }}
|
||||
</h3>
|
||||
<p class="text-gray-600 dark:text-zinc-300 whitespace-pre-wrap leading-relaxed">
|
||||
<p
|
||||
:id="messageId"
|
||||
class="text-gray-600 dark:text-zinc-300 whitespace-pre-wrap leading-relaxed"
|
||||
>
|
||||
{{ pendingConfirm.message }}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -61,21 +74,48 @@ export default {
|
|||
return {
|
||||
pendingConfirm: null,
|
||||
resolvePromise: null,
|
||||
titleId: "confirm-dialog-title",
|
||||
messageId: "confirm-dialog-message",
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
GlobalEmitter.on("confirm", this.show);
|
||||
window.addEventListener("keydown", this.onWindowKeydown);
|
||||
},
|
||||
beforeUnmount() {
|
||||
GlobalEmitter.off("confirm", this.show);
|
||||
window.removeEventListener("keydown", this.onWindowKeydown);
|
||||
},
|
||||
methods: {
|
||||
show({ message, resolve }) {
|
||||
show({ message, title, resolve }) {
|
||||
if (typeof this.resolvePromise === "function") {
|
||||
this.resolvePromise(false);
|
||||
}
|
||||
this.pendingConfirm = { message };
|
||||
this.pendingConfirm = {
|
||||
message,
|
||||
title: typeof title === "string" && title.trim() ? title.trim() : "",
|
||||
};
|
||||
this.resolvePromise = resolve;
|
||||
this.$nextTick(() => {
|
||||
const panel = this.$refs.dialogPanel;
|
||||
if (panel && typeof panel.focus === "function") {
|
||||
panel.focus();
|
||||
}
|
||||
});
|
||||
},
|
||||
onWindowKeydown(event) {
|
||||
if (!this.pendingConfirm) {
|
||||
return;
|
||||
}
|
||||
if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
this.confirm();
|
||||
}
|
||||
},
|
||||
confirm() {
|
||||
if (this.resolvePromise) {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
<button
|
||||
type="button"
|
||||
class="rounded-lg p-1 text-sem-fg-muted hover:bg-sem-surface/60 sm:hidden"
|
||||
:aria-label="$t('common.back')"
|
||||
:title="$t('common.back')"
|
||||
@click="selectedNodeHash = null"
|
||||
>
|
||||
<MaterialDesignIcon icon-name="arrow-left" class="size-5" />
|
||||
|
|
@ -233,6 +235,8 @@ import {
|
|||
import { renderNomadPageByPath, isolateNomadLinksInHtml } from "../../js/NomadPageRenderer.js";
|
||||
import { handleRichHtmlLinkClick } from "../../js/NomadRichHtmlLinks.js";
|
||||
import ArchiveSidebar from "./ArchiveSidebar.vue";
|
||||
import DialogUtils from "../../js/DialogUtils";
|
||||
import ToastUtils from "../../js/ToastUtils";
|
||||
|
||||
export default {
|
||||
name: "ArchivesPage",
|
||||
|
|
@ -457,7 +461,11 @@ export default {
|
|||
async deleteSelected() {
|
||||
if (this.selectedArchives.length === 0) return;
|
||||
|
||||
if (!confirm(`Are you sure you want to delete ${this.selectedArchives.length} selected snapshots?`)) {
|
||||
if (
|
||||
!(await DialogUtils.confirm(
|
||||
this.$t("archives.delete_selected_confirm", { count: this.selectedArchives.length })
|
||||
))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -480,11 +488,11 @@ export default {
|
|||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to delete archives:", e);
|
||||
alert("Failed to delete snapshots. Please try again.");
|
||||
ToastUtils.error(this.$t("archives.failed_delete"));
|
||||
}
|
||||
},
|
||||
async deleteArchive(archive) {
|
||||
if (!confirm("Are you sure you want to delete this snapshot?")) {
|
||||
if (!(await DialogUtils.confirm(this.$t("archives.delete_snapshot_confirm")))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -507,7 +515,7 @@ export default {
|
|||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to delete archive:", e);
|
||||
alert("Failed to delete snapshot. Please try again.");
|
||||
ToastUtils.error(this.$t("archives.failed_delete"));
|
||||
}
|
||||
},
|
||||
viewArchive(archive) {
|
||||
|
|
|
|||
|
|
@ -29,9 +29,11 @@
|
|||
class="size-8 text-red-600 dark:text-red-400"
|
||||
/>
|
||||
</div>
|
||||
<h2 class="text-xl font-bold text-gray-900 dark:text-white mb-2">LXST is disabled</h2>
|
||||
<h2 class="text-xl font-bold text-gray-900 dark:text-white mb-2">
|
||||
{{ $t("call.lxst_disabled_title") }}
|
||||
</h2>
|
||||
<p class="text-sm text-gray-500 dark:text-zinc-400 mb-6">
|
||||
Telephony is currently disabled. Enable it to make and receive calls.
|
||||
{{ $t("call.lxst_disabled_body") }}
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -39,7 +41,7 @@
|
|||
@click="updateConfig({ telephone_enabled: true })"
|
||||
>
|
||||
<MaterialDesignIcon icon-name="phone" class="size-5" />
|
||||
Enable LXST
|
||||
{{ $t("call.enable_lxst") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -598,7 +600,7 @@
|
|||
<input
|
||||
v-model="destinationHash"
|
||||
type="text"
|
||||
placeholder="Identity Hash or Name"
|
||||
:placeholder="$t('call.identity_or_name')"
|
||||
class="input-field"
|
||||
@keydown.enter.prevent="handleCallInputEnter"
|
||||
@keydown.up.prevent="handleCallInputUp"
|
||||
|
|
@ -1440,7 +1442,7 @@
|
|||
<input
|
||||
v-model="recordingSearch"
|
||||
type="text"
|
||||
placeholder="Search recordings..."
|
||||
:placeholder="$t('call.search_recordings')"
|
||||
class="block w-full rounded-lg border-0 py-2 pl-10 text-gray-900 dark:text-white shadow-xs ring-1 ring-inset ring-gray-300 dark:ring-zinc-800 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm dark:bg-zinc-900"
|
||||
@input="onRecordingSearchInput"
|
||||
/>
|
||||
|
|
@ -1691,20 +1693,20 @@
|
|||
v-model="contactForm.lxmf_address"
|
||||
type="text"
|
||||
class="input-field font-mono text-xs"
|
||||
placeholder="Optional"
|
||||
:placeholder="$t('common.optional')"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
class="block text-xs font-bold text-gray-500 dark:text-zinc-400 uppercase tracking-wider mb-1.5 ml-1"
|
||||
>
|
||||
LXST Address
|
||||
{{ $t("identities.lxst_address") }}
|
||||
</label>
|
||||
<input
|
||||
v-model="contactForm.lxst_address"
|
||||
type="text"
|
||||
class="input-field font-mono text-xs"
|
||||
placeholder="Optional"
|
||||
:placeholder="$t('common.optional')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1757,6 +1759,7 @@ import MaterialDesignIcon from "../MaterialDesignIcon.vue";
|
|||
import LxmfUserIcon from "../LxmfUserIcon.vue";
|
||||
import Toggle from "../forms/Toggle.vue";
|
||||
import ToastUtils from "../../js/ToastUtils";
|
||||
import DialogUtils from "../../js/DialogUtils";
|
||||
import {
|
||||
WEB_AUDIO_MIC_TOAST_KEY,
|
||||
classifyGetUserMediaError,
|
||||
|
|
@ -3277,13 +3280,13 @@ export default {
|
|||
if (this.config) {
|
||||
this.config.call_recording_enabled = value;
|
||||
}
|
||||
ToastUtils.success(value ? "Call recording enabled" : "Call recording disabled");
|
||||
ToastUtils.success(value ? this.$t("call.recording_enabled") : this.$t("call.recording_disabled"));
|
||||
} catch {
|
||||
ToastUtils.error(this.$t("call.failed_to_update_recording_status"));
|
||||
}
|
||||
},
|
||||
async clearHistory() {
|
||||
if (!confirm(this.$t("common.delete_confirm"))) return;
|
||||
if (!(await DialogUtils.confirm(this.$t("common.delete_confirm")))) return;
|
||||
try {
|
||||
await window.api.delete("/api/v1/telephone/history");
|
||||
this.callHistory = [];
|
||||
|
|
@ -3303,7 +3306,7 @@ export default {
|
|||
}
|
||||
},
|
||||
async blockIdentity(hash) {
|
||||
if (!confirm(`Are you sure you want to banish this identity?`)) return;
|
||||
if (!(await DialogUtils.confirm(this.$t("call.banish_identity_confirm")))) return;
|
||||
try {
|
||||
await window.api.post("/api/v1/blocked-destinations", {
|
||||
destination_hash: hash,
|
||||
|
|
@ -3339,7 +3342,7 @@ export default {
|
|||
}
|
||||
},
|
||||
async deleteRingtone(ringtone) {
|
||||
if (!confirm(this.$t("common.delete_confirm"))) return;
|
||||
if (!(await DialogUtils.confirm(this.$t("common.delete_confirm")))) return;
|
||||
try {
|
||||
await window.api.delete(`/api/v1/telephone/ringtones/${ringtone.id}`);
|
||||
ToastUtils.success(this.$t("call.ringtone_deleted"));
|
||||
|
|
@ -3578,11 +3581,11 @@ export default {
|
|||
this.isContactModalOpen = false;
|
||||
this.getContacts();
|
||||
} catch (e) {
|
||||
ToastUtils.error(e.response?.data?.message || "Failed to save contact");
|
||||
ToastUtils.error(e.response?.data?.message || this.$t("call.failed_to_save_contact"));
|
||||
}
|
||||
},
|
||||
async deleteContact(contactId) {
|
||||
if (!confirm("Are you sure you want to delete this contact?")) return;
|
||||
if (!(await DialogUtils.confirm(this.$t("call.delete_contact_confirm")))) return;
|
||||
try {
|
||||
await window.api.delete(`/api/v1/telephone/contacts/${contactId}`);
|
||||
ToastUtils.success(this.$t("call.contact_deleted"));
|
||||
|
|
@ -3629,7 +3632,7 @@ export default {
|
|||
ToastUtils.success(this.$t("call.greeting_generated_successfully"));
|
||||
await this.getVoicemailStatus();
|
||||
} catch (e) {
|
||||
ToastUtils.error(e.response?.data?.message || "Failed to generate greeting");
|
||||
ToastUtils.error(e.response?.data?.message || this.$t("call.failed_to_generate_greeting"));
|
||||
} finally {
|
||||
this.isGeneratingGreeting = false;
|
||||
}
|
||||
|
|
@ -3651,14 +3654,14 @@ export default {
|
|||
ToastUtils.success(this.$t("call.greeting_uploaded_successfully"));
|
||||
await this.getVoicemailStatus();
|
||||
} catch (e) {
|
||||
ToastUtils.error(e.response?.data?.message || "Failed to upload greeting");
|
||||
ToastUtils.error(e.response?.data?.message || this.$t("call.failed_to_upload_greeting"));
|
||||
} finally {
|
||||
this.isUploadingGreeting = false;
|
||||
event.target.value = "";
|
||||
}
|
||||
},
|
||||
async deleteGreeting() {
|
||||
if (!confirm("Are you sure you want to delete your custom greeting?")) return;
|
||||
if (!(await DialogUtils.confirm(this.$t("call.delete_greeting_confirm")))) return;
|
||||
|
||||
try {
|
||||
await window.api.delete("/api/v1/telephone/voicemail/greeting");
|
||||
|
|
@ -3796,7 +3799,7 @@ export default {
|
|||
}
|
||||
},
|
||||
async deleteRecording(recordingId) {
|
||||
if (!confirm("Are you sure you want to delete this recording?")) return;
|
||||
if (!(await DialogUtils.confirm(this.$t("call.delete_recording_confirm")))) return;
|
||||
try {
|
||||
await window.api.delete(`/api/v1/telephone/recordings/${recordingId}`);
|
||||
this.getRecordings();
|
||||
|
|
@ -3873,7 +3876,7 @@ export default {
|
|||
await window.api.post(`/api/v1/telephone/call/${hashToCall}`);
|
||||
} catch (e) {
|
||||
this.initiationStatus = null;
|
||||
ToastUtils.error(e.response?.data?.message || "Failed to initiate call");
|
||||
ToastUtils.error(e.response?.data?.message || this.$t("call.failed_to_initiate_call"));
|
||||
}
|
||||
},
|
||||
handleCallInputUp() {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<input
|
||||
:value="contactsSearch"
|
||||
type="text"
|
||||
placeholder="Search contacts..."
|
||||
:placeholder="$t('contacts.search_placeholder')"
|
||||
class="block w-full rounded-lg border-0 py-2 pl-10 text-gray-900 dark:text-white shadow-xs ring-1 ring-inset ring-gray-300 dark:ring-zinc-800 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm dark:bg-zinc-900"
|
||||
@input="onSearchInput"
|
||||
/>
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
@click="$emit('add')"
|
||||
>
|
||||
<MaterialDesignIcon icon-name="plus" class="size-5" />
|
||||
Add
|
||||
{{ $t("common.add") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -29,8 +29,8 @@
|
|||
<div class="bg-gray-200 dark:bg-zinc-800 p-6 rounded-full inline-block mb-4">
|
||||
<MaterialDesignIcon icon-name="account-multiple" class="size-12 text-gray-400" />
|
||||
</div>
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-white">No Contacts</h3>
|
||||
<p class="text-gray-500 dark:text-zinc-400">Add contacts to quickly call them.</p>
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-white">{{ $t("contacts.no_contacts") }}</h3>
|
||||
<p class="text-gray-500 dark:text-zinc-400">{{ $t("call.no_contacts_hint") }}</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="space-y-4">
|
||||
|
|
@ -64,15 +64,21 @@
|
|||
<span
|
||||
v-if="contact.preferred_ringtone_id"
|
||||
class="text-[9px] px-1.5 py-0.5 rounded-sm bg-amber-50 dark:bg-amber-900/30 text-amber-600 dark:text-amber-400 border border-amber-100 dark:border-amber-800/50 flex items-center gap-1"
|
||||
title="Custom Ringtone Set"
|
||||
:title="$t('call.custom_ringtone_set')"
|
||||
>
|
||||
<MaterialDesignIcon icon-name="music" class="size-2.5" />
|
||||
{{ contact.preferred_ringtone_id === -1 ? "Random" : "Custom" }}
|
||||
{{
|
||||
contact.preferred_ringtone_id === -1
|
||||
? $t("call.random")
|
||||
: $t("call.custom")
|
||||
}}
|
||||
</span>
|
||||
<div class="flex items-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
class="p-1.5 text-gray-400 hover:text-blue-500 transition-colors"
|
||||
:aria-label="$t('common.edit')"
|
||||
:title="$t('common.edit')"
|
||||
@click="$emit('edit', contact)"
|
||||
>
|
||||
<MaterialDesignIcon icon-name="pencil" class="size-4" />
|
||||
|
|
@ -80,6 +86,8 @@
|
|||
<button
|
||||
type="button"
|
||||
class="p-1.5 text-gray-400 hover:text-red-500 transition-colors"
|
||||
:aria-label="$t('common.delete')"
|
||||
:title="$t('common.delete')"
|
||||
@click="$emit('delete', contact.id)"
|
||||
>
|
||||
<MaterialDesignIcon icon-name="delete" class="size-4" />
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<input
|
||||
:value="voicemailSearch"
|
||||
type="text"
|
||||
placeholder="Search voicemails..."
|
||||
:placeholder="$t('call.search_voicemails')"
|
||||
class="block w-full rounded-lg border-0 py-2 pl-10 text-gray-900 dark:text-white shadow-xs ring-1 ring-inset ring-gray-300 dark:ring-zinc-800 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm dark:bg-zinc-900"
|
||||
@input="onSearchInput"
|
||||
/>
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
<div class="flex items-center gap-2">
|
||||
<MaterialDesignIcon icon-name="cog" class="size-5 text-blue-500" />
|
||||
<h3 class="text-sm font-bold text-gray-900 dark:text-white uppercase tracking-wider">
|
||||
Voicemail Settings
|
||||
{{ $t("call.voicemail_settings") }}
|
||||
</h3>
|
||||
</div>
|
||||
<MaterialDesignIcon
|
||||
|
|
@ -79,7 +79,7 @@
|
|||
:value="config.voicemail_greeting"
|
||||
rows="3"
|
||||
class="block w-full rounded-lg border-0 py-2 text-gray-900 dark:text-white shadow-xs ring-1 ring-inset ring-gray-300 dark:ring-zinc-800 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm sm:leading-6 dark:bg-zinc-900"
|
||||
placeholder="Enter greeting text..."
|
||||
:placeholder="$t('call.enter_greeting_text')"
|
||||
@input="$emit('patch-config', { voicemail_greeting: $event.target.value })"
|
||||
></textarea>
|
||||
|
||||
|
|
|
|||
|
|
@ -749,9 +749,12 @@ export default {
|
|||
const duplicates = this.contacts.filter((c) => c.name === contact.name && c.id !== contact.id);
|
||||
const confirmMsg =
|
||||
duplicates.length > 0
|
||||
? `${this.$t("contacts.remove_contact_confirm")}\n\n(${duplicates.length} additional duplicate${duplicates.length > 1 ? "s" : ""} named "${contact.name}" will also be removed)`
|
||||
? this.$t("contacts.remove_duplicates_confirm", {
|
||||
count: duplicates.length,
|
||||
name: contact.name,
|
||||
})
|
||||
: this.$t("contacts.remove_contact_confirm");
|
||||
if (!window.confirm(confirmMsg)) return;
|
||||
if (!(await DialogUtils.confirm(confirmMsg))) return;
|
||||
try {
|
||||
const ids = [contact.id, ...duplicates.map((c) => c.id)];
|
||||
for (const id of ids) {
|
||||
|
|
|
|||
|
|
@ -952,6 +952,7 @@ import ContextMenuItem from "../contextmenu/ContextMenuItem.vue";
|
|||
import ContextMenuPanel from "../contextmenu/ContextMenuPanel.vue";
|
||||
import DOMPurify from "dompurify";
|
||||
import ToastUtils from "../../js/ToastUtils";
|
||||
import DialogUtils from "../../js/DialogUtils";
|
||||
import TileCache from "../../js/TileCache";
|
||||
import { mapViewStateKey } from "../../js/mapStateKeys.js";
|
||||
import GlobalState from "../../js/GlobalState";
|
||||
|
|
@ -1560,7 +1561,7 @@ export default {
|
|||
}
|
||||
},
|
||||
async deleteMBTiles(filename) {
|
||||
if (!confirm(`Are you sure you want to delete ${filename}?`)) return;
|
||||
if (!(await DialogUtils.confirm(this.$t("map.delete_file_confirm", { name: filename })))) return;
|
||||
try {
|
||||
await window.api.delete(`/api/v1/map/mbtiles/${filename}`);
|
||||
await this.loadMBTilesList();
|
||||
|
|
@ -3837,24 +3838,24 @@ export default {
|
|||
this.stopMeasuring();
|
||||
},
|
||||
|
||||
clearDrawings() {
|
||||
if (confirm("Clear all drawings from the map?")) {
|
||||
this.drawSource.clear();
|
||||
if (this.select) {
|
||||
this.select.getFeatures().clear();
|
||||
}
|
||||
this.selectedFeature = null;
|
||||
this.syncDrawFeatureInfoOverlay();
|
||||
// clear tooltips if any
|
||||
const overlays = this.map.getOverlays().getArray();
|
||||
for (let i = overlays.length - 1; i >= 0; i--) {
|
||||
const overlay = overlays[i];
|
||||
if (overlay.get("isMeasureTooltip")) {
|
||||
this.map.removeOverlay(overlay);
|
||||
}
|
||||
}
|
||||
this.saveMapState();
|
||||
async clearDrawings() {
|
||||
if (!(await DialogUtils.confirm(this.$t("map.clear_drawings_confirm")))) {
|
||||
return;
|
||||
}
|
||||
this.drawSource.clear();
|
||||
if (this.select) {
|
||||
this.select.getFeatures().clear();
|
||||
}
|
||||
this.selectedFeature = null;
|
||||
this.syncDrawFeatureInfoOverlay();
|
||||
const overlays = this.map.getOverlays().getArray();
|
||||
for (let i = overlays.length - 1; i >= 0; i--) {
|
||||
const overlay = overlays[i];
|
||||
if (overlay.get("isMeasureTooltip")) {
|
||||
this.map.removeOverlay(overlay);
|
||||
}
|
||||
}
|
||||
this.saveMapState();
|
||||
},
|
||||
|
||||
// Measurement methods
|
||||
|
|
@ -4667,7 +4668,7 @@ export default {
|
|||
},
|
||||
|
||||
async deleteDrawing(drawing) {
|
||||
if (!confirm(`Delete drawing "${drawing.name}"?`)) return;
|
||||
if (!(await DialogUtils.confirm(this.$t("map.delete_drawing_confirm", { name: drawing.name })))) return;
|
||||
try {
|
||||
await window.api.delete(`/api/v1/map/drawings/${drawing.id}`);
|
||||
this.savedDrawings = this.savedDrawings.filter((d) => d.id !== drawing.id);
|
||||
|
|
@ -5149,10 +5150,12 @@ export default {
|
|||
const t = this.telemetryList.find((t) => t.destination_hash === hash);
|
||||
if (t) t.is_tracking = response.data.is_tracking;
|
||||
|
||||
ToastUtils.success(response.data.is_tracking ? "Live tracking enabled" : "Live tracking disabled");
|
||||
ToastUtils.success(
|
||||
response.data.is_tracking ? this.$t("map.tracking_enabled") : this.$t("map.tracking_disabled")
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("Failed to toggle tracking", e);
|
||||
ToastUtils.error("Failed to update tracking status");
|
||||
ToastUtils.error(this.$t("map.failed_update_tracking"));
|
||||
}
|
||||
},
|
||||
async toggleDiscoveredNodes() {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@
|
|||
<div v-if="loading" class="flex justify-center py-4">
|
||||
<v-icon icon="mdi-loading" class="animate-spin text-gray-400" size="20"></v-icon>
|
||||
</div>
|
||||
<div v-else-if="messages.length === 0" class="text-center py-4 text-xs text-gray-400">No messages yet</div>
|
||||
<div v-else-if="messages.length === 0" class="text-center py-4 text-xs text-gray-400">
|
||||
{{ $t("messages.no_messages_yet") }}
|
||||
</div>
|
||||
<div
|
||||
v-for="msg in messages"
|
||||
:key="msg.hash"
|
||||
|
|
@ -30,9 +32,11 @@
|
|||
class="flex items-center gap-1 mb-1 pb-1 border-b border-white/10 opacity-80"
|
||||
>
|
||||
<v-icon icon="mdi-satellite-variant" size="10"></v-icon>
|
||||
<span class="text-[8px] font-bold uppercase tracking-wider"
|
||||
>{{ msg.is_outbound ? "Sent" : "Received" }} Telemetry</span
|
||||
>
|
||||
<span class="text-[8px] font-bold uppercase tracking-wider">{{
|
||||
msg.is_outbound
|
||||
? $t("messages.telemetry_label_sent")
|
||||
: $t("messages.telemetry_label_received")
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
|
@ -40,7 +44,9 @@
|
|||
class="flex items-center gap-1 mb-1 pb-1 border-b border-white/10 opacity-80"
|
||||
>
|
||||
<v-icon icon="mdi-crosshairs-question" size="10"></v-icon>
|
||||
<span class="text-[8px] font-bold uppercase tracking-wider">Location Request</span>
|
||||
<span class="text-[8px] font-bold uppercase tracking-wider">{{
|
||||
$t("messages.telemetry_location_request")
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="msg.content" class="leading-normal">{{ msg.content }}</div>
|
||||
|
|
@ -82,11 +88,14 @@
|
|||
v-model="newMessage"
|
||||
type="text"
|
||||
class="flex-1 bg-gray-50 dark:bg-zinc-800 border border-gray-200 dark:border-zinc-700 rounded-md px-2 py-1 text-xs focus:outline-hidden focus:ring-1 focus:ring-blue-500 text-gray-900 dark:text-zinc-100"
|
||||
placeholder="Type a message..."
|
||||
:placeholder="$t('messages.send_placeholder')"
|
||||
@keydown.enter="sendMessage"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
:disabled="!newMessage.trim() || sending"
|
||||
:aria-label="$t('messages.send')"
|
||||
:title="$t('messages.send')"
|
||||
class="p-1.5 bg-blue-500 hover:bg-blue-600 disabled:bg-gray-300 dark:disabled:bg-zinc-700 text-white rounded-md transition-colors"
|
||||
@click="sendMessage"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -321,11 +321,7 @@ export default {
|
|||
}
|
||||
},
|
||||
async onBlockDestination() {
|
||||
if (
|
||||
!(await DialogUtils.confirm(
|
||||
"Are you sure you want to banish this user? They will not be able to send you messages or establish links."
|
||||
))
|
||||
) {
|
||||
if (!(await DialogUtils.confirm(this.$t("messages.banish_confirm")))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1340,7 +1340,7 @@
|
|||
v-if="!translateTargetSelectOptions.length"
|
||||
class="text-xs text-amber-700/90 dark:text-amber-300/90 -mt-0.5"
|
||||
>
|
||||
No translation languages available yet. Check the translator tool in Tools.
|
||||
{{ $t("messages.translate_no_languages") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -3975,12 +3975,7 @@ export default {
|
|||
},
|
||||
async onBanishHeaderClick() {
|
||||
if (!this.selectedPeer) return;
|
||||
if (
|
||||
!(await DialogUtils.confirm(
|
||||
this.$t("messages.banish_confirm") ||
|
||||
"Are you sure you want to banish this user? They will not be able to send you messages or establish links."
|
||||
))
|
||||
) {
|
||||
if (!(await DialogUtils.confirm(this.$t("messages.banish_confirm")))) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
|
@ -5425,12 +5420,7 @@ export default {
|
|||
async deleteChatItem(chatItem, shouldConfirm = true) {
|
||||
try {
|
||||
// ask user to confirm deleting message
|
||||
if (
|
||||
shouldConfirm &&
|
||||
!(await DialogUtils.confirm(
|
||||
"Are you sure you want to delete this message? This can not be undone!"
|
||||
))
|
||||
) {
|
||||
if (shouldConfirm && !(await DialogUtils.confirm(this.$t("messages.delete_message_confirm")))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -5558,7 +5548,7 @@ export default {
|
|||
if (totalMessageSize > 1000 * 900) {
|
||||
if (
|
||||
!(await DialogUtils.confirm(
|
||||
`Your message exceeds 900KB (It's ${this.formatBytes(totalMessageSize)}). It may be rejected by the recipient unless they have increased their delivery limit. Do you want to try sending anyway?`
|
||||
this.$t("messages.send_oversized_confirm", { size: this.formatBytes(totalMessageSize) })
|
||||
))
|
||||
) {
|
||||
return null;
|
||||
|
|
@ -5884,11 +5874,7 @@ export default {
|
|||
);
|
||||
if (failedItems.length === 0) return;
|
||||
|
||||
if (
|
||||
!(await DialogUtils.confirm(
|
||||
`Are you sure you want to retry sending all ${failedItems.length} failed/cancelled messages?`
|
||||
))
|
||||
) {
|
||||
if (!(await DialogUtils.confirm(this.$t("messages.retry_failed_confirm", { count: failedItems.length })))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -6197,10 +6183,12 @@ export default {
|
|||
destination_hash: hash,
|
||||
is_tracking: response.data.is_tracking,
|
||||
});
|
||||
ToastUtils.success(response.data.is_tracking ? "Live tracking enabled" : "Live tracking disabled");
|
||||
ToastUtils.success(
|
||||
response.data.is_tracking ? this.$t("map.tracking_enabled") : this.$t("map.tracking_disabled")
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("Failed to toggle tracking", e);
|
||||
ToastUtils.error("Failed to update tracking status");
|
||||
ToastUtils.error(this.$t("map.failed_update_tracking"));
|
||||
}
|
||||
},
|
||||
formatTimeAgo: function (datetimeString) {
|
||||
|
|
@ -6280,7 +6268,7 @@ export default {
|
|||
try {
|
||||
await window.api.post(`/api/v1/telephone/call/${this.selectedPeer.destination_hash}`);
|
||||
} catch (e) {
|
||||
const message = e.response?.data?.message ?? "Failed to start call";
|
||||
const message = e.response?.data?.message ?? this.$t("call.failed_to_initiate_call");
|
||||
DialogUtils.alert(message);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1207,8 +1207,8 @@ export default {
|
|||
async onBulkDelete(destination_hashes) {
|
||||
try {
|
||||
const confirmed = await DialogUtils.confirm(
|
||||
"Are you sure you want to delete these conversations? All messages will be lost.",
|
||||
"Delete Conversations"
|
||||
this.$t("messages.delete_conversations_confirm"),
|
||||
this.$t("messages.delete_conversations_title")
|
||||
);
|
||||
if (!confirmed) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@
|
|||
import MaterialDesignIcon from "../MaterialDesignIcon.vue";
|
||||
import Toggle from "../forms/Toggle.vue";
|
||||
import ToastUtils from "../../js/ToastUtils";
|
||||
import DialogUtils from "../../js/DialogUtils";
|
||||
import NotificationSoundUtils from "../../js/NotificationSoundUtils";
|
||||
|
||||
export default {
|
||||
|
|
@ -225,7 +226,7 @@ export default {
|
|||
}
|
||||
},
|
||||
async deleteSound(sound) {
|
||||
if (!confirm(this.$t("common.delete_confirm"))) {
|
||||
if (!(await DialogUtils.confirm(this.$t("common.delete_confirm")))) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -363,9 +363,9 @@ export default {
|
|||
};
|
||||
this.sidebandPlugins = response.data?.plugins || [];
|
||||
},
|
||||
onSidebandMasterToggle() {
|
||||
async onSidebandMasterToggle() {
|
||||
if (this.sidebandConfig.service_plugins_enabled) {
|
||||
const ok = window.confirm(this.$t("plugins.sideband.danger_confirm"));
|
||||
const ok = await DialogUtils.confirm(this.$t("plugins.sideband.danger_confirm"));
|
||||
if (!ok) {
|
||||
this.sidebandConfig.service_plugins_enabled = false;
|
||||
}
|
||||
|
|
@ -461,9 +461,9 @@ export default {
|
|||
this.busyPluginId = null;
|
||||
}
|
||||
},
|
||||
confirmRemove(plugin) {
|
||||
async confirmRemove(plugin) {
|
||||
const prompt = this.$t("plugins.settings.confirm_remove", { name: plugin.name || plugin.id });
|
||||
if (!window.confirm(prompt)) {
|
||||
if (!(await DialogUtils.confirm(prompt))) {
|
||||
return;
|
||||
}
|
||||
void this.removePlugin(plugin.id);
|
||||
|
|
|
|||
|
|
@ -3818,7 +3818,7 @@ export default {
|
|||
this.getTrustedTelemetryPeers();
|
||||
ToastUtils.success(this.$t("app.telemetry_trust_revoked", { name: peer.name }));
|
||||
} catch (e) {
|
||||
ToastUtils.error("Failed to revoke telemetry trust");
|
||||
ToastUtils.error(this.$t("app.telemetry_trust_failed"));
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
|
|
@ -4900,9 +4900,9 @@ export default {
|
|||
try {
|
||||
const newConfig = await patchServerConfig({ telephone_enabled: value }, window.api);
|
||||
this.config = newConfig;
|
||||
ToastUtils.success(value ? "Telephone enabled" : "Telephone disabled");
|
||||
ToastUtils.success(value ? this.$t("call.telephony_enabled") : this.$t("call.telephony_disabled"));
|
||||
} catch {
|
||||
ToastUtils.error("Failed to update telephone setting");
|
||||
ToastUtils.error(this.$t("call.failed_to_update_call_settings"));
|
||||
}
|
||||
},
|
||||
async onDesktopOpenCallsInSeparateWindowChange(value) {
|
||||
|
|
@ -4976,11 +4976,7 @@ export default {
|
|||
}, 1000);
|
||||
},
|
||||
async flushArchivedPages() {
|
||||
if (
|
||||
!(await DialogUtils.confirm(
|
||||
"Are you sure you want to delete all archived pages? This cannot be undone."
|
||||
))
|
||||
) {
|
||||
if (!(await DialogUtils.confirm(this.$t("settings.flush_archived_pages_confirm")))) {
|
||||
return;
|
||||
}
|
||||
WebSocketConnection.send(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<section v-show="visible" class="settings-section break-inside-avoid">
|
||||
<header class="settings-section__header">
|
||||
<div>
|
||||
<div class="settings-section__eyebrow">Personalise</div>
|
||||
<div class="settings-section__eyebrow">{{ $t("app.appearance") }}</div>
|
||||
<h2>{{ $t("app.appearance") }}</h2>
|
||||
<p>{{ $t("app.appearance_description") }}</p>
|
||||
</div>
|
||||
|
|
@ -49,7 +49,9 @@
|
|||
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">Message Font Size</div>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $t("app.message_font_size") }}
|
||||
</div>
|
||||
<div class="text-xs font-mono text-blue-500 dark:text-blue-400">
|
||||
{{ config.message_font_size || 14 }}px
|
||||
</div>
|
||||
|
|
@ -71,7 +73,9 @@
|
|||
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">Icon Size</div>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $t("app.message_icon_size") }}
|
||||
</div>
|
||||
<div class="text-xs font-mono text-blue-500 dark:text-blue-400">
|
||||
{{ config.message_icon_size || 28 }}px
|
||||
</div>
|
||||
|
|
@ -257,7 +261,9 @@
|
|||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">Outbound Color</div>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $t("settings.outbound_bubble_color") }}
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<input
|
||||
:value="config.message_outbound_bubble_color"
|
||||
|
|
@ -275,7 +281,9 @@
|
|||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">Failed Color</div>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $t("settings.failed_bubble_color") }}
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<input
|
||||
:value="config.message_failed_bubble_color"
|
||||
|
|
@ -293,7 +301,9 @@
|
|||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">Waiting Color</div>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $t("settings.waiting_bubble_color") }}
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<input
|
||||
:value="config.message_waiting_bubble_color"
|
||||
|
|
@ -313,14 +323,16 @@
|
|||
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">Inbound Color (Optional)</div>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $t("settings.inbound_bubble_color") }}
|
||||
</div>
|
||||
<button
|
||||
v-if="config.message_inbound_bubble_color"
|
||||
type="button"
|
||||
class="text-[10px] text-red-500 font-bold uppercase hover:underline"
|
||||
@click="onInboundBubbleReset"
|
||||
>
|
||||
Reset to default
|
||||
{{ $t("settings.inbound_bubble_reset") }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
|
|
@ -335,12 +347,13 @@
|
|||
v-if="!config.message_inbound_bubble_color"
|
||||
class="flex-1 flex items-center px-3 text-xs text-gray-400 bg-gray-50 dark:bg-zinc-900 rounded-xl border border-dashed border-gray-200 dark:border-zinc-800 italic"
|
||||
>
|
||||
Using theme default. Click to customize ->
|
||||
{{ $t("settings.inbound_bubble_default_hint") }}
|
||||
<button
|
||||
type="button"
|
||||
class="ml-2 px-2 py-1 bg-blue-500 text-white rounded-lg not-italic font-bold"
|
||||
@click="onInboundBubbleCustomize"
|
||||
>
|
||||
Customize
|
||||
{{ $t("settings.inbound_bubble_customize") }}
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
|
|
|
|||
|
|
@ -400,6 +400,7 @@
|
|||
|
||||
<script>
|
||||
import ToastUtils from "../../js/ToastUtils";
|
||||
import DialogUtils from "../../js/DialogUtils";
|
||||
import DownloadUtils from "../../js/DownloadUtils";
|
||||
import MaterialDesignIcon from "../MaterialDesignIcon.vue";
|
||||
import ToolsPageHeader from "./ToolsPageHeader.vue";
|
||||
|
|
@ -521,7 +522,7 @@ export default {
|
|||
}
|
||||
},
|
||||
async deleteBot(botId) {
|
||||
if (!confirm(this.$t("common.delete_confirm"))) return;
|
||||
if (!(await DialogUtils.confirm(this.$t("common.delete_confirm")))) return;
|
||||
try {
|
||||
await window.api.post("/api/v1/bots/delete", { bot_id: botId });
|
||||
ToastUtils.success(this.$t("bots.bot_deleted"));
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ import WifiTransport from "../../js/rnode/transports/WifiTransport.js";
|
|||
import { diagnose } from "../../js/rnode/Diagnostics.js";
|
||||
|
||||
import ToastUtils from "../../js/ToastUtils.js";
|
||||
import DialogUtils from "../../js/DialogUtils.js";
|
||||
import ToolsPageHeader from "./ToolsPageHeader.vue";
|
||||
import { rnodeIntegrityKeyForSrc } from "../../js/rnode/rnodeIntegrityKey.js";
|
||||
|
||||
|
|
@ -584,7 +585,7 @@ export default {
|
|||
}
|
||||
},
|
||||
async wipeEeprom() {
|
||||
if (!confirm(this.$t("tools.rnode_flasher.alerts.eeprom_wipe_confirm"))) {
|
||||
if (!(await DialogUtils.confirm(this.$t("tools.rnode_flasher.alerts.eeprom_wipe_confirm")))) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@
|
|||
<script>
|
||||
import MaterialDesignIcon from "../MaterialDesignIcon.vue";
|
||||
import ToastUtils from "../../js/ToastUtils";
|
||||
import DialogUtils from "../../js/DialogUtils";
|
||||
import ToolsPageHeader from "./ToolsPageHeader.vue";
|
||||
|
||||
export default {
|
||||
|
|
@ -365,7 +366,7 @@ export default {
|
|||
}
|
||||
},
|
||||
async deleteUpload(name) {
|
||||
if (!window.confirm(this.$t("tools.repository_server.delete_confirm", { name }))) {
|
||||
if (!(await DialogUtils.confirm(this.$t("tools.repository_server.delete_confirm", { name })))) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -3,32 +3,24 @@ import GlobalEmitter from "./GlobalEmitter";
|
|||
class DialogUtils {
|
||||
static alert(message, type = "info") {
|
||||
if (window.electron) {
|
||||
// running inside electron, use ipc alert
|
||||
window.electron.alert(message);
|
||||
}
|
||||
|
||||
// always show toast as well (or instead of browser alert)
|
||||
GlobalEmitter.emit("toast", { message, type });
|
||||
}
|
||||
|
||||
static confirm(message) {
|
||||
if (window.electron) {
|
||||
// running inside electron, use ipc confirm
|
||||
return window.electron.confirm(message);
|
||||
} else {
|
||||
// running inside normal browser, use custom confirm dialog
|
||||
return new Promise((resolve) => {
|
||||
GlobalEmitter.emit("confirm", { message, resolve });
|
||||
});
|
||||
}
|
||||
static confirm(message, title) {
|
||||
return new Promise((resolve) => {
|
||||
const payload = { message, resolve };
|
||||
if (typeof title === "string" && title.trim()) {
|
||||
payload.title = title.trim();
|
||||
}
|
||||
GlobalEmitter.emit("confirm", payload);
|
||||
});
|
||||
}
|
||||
|
||||
// Always use the in-app confirm dialog, even inside electron, for
|
||||
// callers that want a themed dialog instead of the native OS prompt.
|
||||
static confirmCustom(message) {
|
||||
return new Promise((resolve) => {
|
||||
GlobalEmitter.emit("confirm", { message, resolve });
|
||||
});
|
||||
static confirmCustom(message, title) {
|
||||
return DialogUtils.confirm(message, title);
|
||||
}
|
||||
|
||||
static async prompt(message, defaultValue = "", options = {}) {
|
||||
|
|
|
|||
|
|
@ -596,7 +596,9 @@
|
|||
"copy": "Kopieren",
|
||||
"copy_to_clipboard": "In die Zwischenablage kopieren",
|
||||
"clear": "Löschen",
|
||||
"prompt_title": "Wert eingeben"
|
||||
"prompt_title": "Wert eingeben",
|
||||
"optional": "Optional",
|
||||
"back": "Zurück"
|
||||
},
|
||||
"stickers": {
|
||||
"settings_title": "Sticker",
|
||||
|
|
@ -1095,6 +1097,7 @@
|
|||
"failed_add_contact": "Kontakt konnte nicht hinzugefügt werden",
|
||||
"remove_contact": "Kontakt entfernen",
|
||||
"remove_contact_confirm": "Diesen Kontakt entfernen?",
|
||||
"remove_duplicates_confirm": "Diesen Kontakt entfernen?\n\n{count} weitere Duplikat(e) namens \"{name}\" werden ebenfalls entfernt.",
|
||||
"contact_removed": "Kontakt entfernt",
|
||||
"failed_remove_contact": "Kontakt konnte nicht entfernt werden",
|
||||
"share_contact": "Kontakt-URI teilen",
|
||||
|
|
@ -1595,6 +1598,12 @@
|
|||
"source_updated": "Kartenquelle aktualisiert",
|
||||
"failed_set_active": "Aktive Karte setzen fehlgeschlagen",
|
||||
"file_deleted": "Datei gelöscht",
|
||||
"delete_file_confirm": "{name} löschen?",
|
||||
"clear_drawings_confirm": "Alle Zeichnungen von der Karte löschen?",
|
||||
"delete_drawing_confirm": "Zeichnung \"{name}\" löschen?",
|
||||
"tracking_enabled": "Live-Verfolgung aktiviert",
|
||||
"tracking_disabled": "Live-Verfolgung deaktiviert",
|
||||
"failed_update_tracking": "Verfolgungsstatus konnte nicht aktualisiert werden",
|
||||
"failed_delete_file": "Datei löschen fehlgeschlagen",
|
||||
"storage_saved": "Speicherverzeichnis gespeichert",
|
||||
"failed_save_storage": "Verzeichnis speichern fehlgeschlagen",
|
||||
|
|
@ -1929,6 +1938,11 @@
|
|||
"marked_all_read": "Alle Konversationen als gelesen markiert",
|
||||
"failed_mark_read": "Als gelesen markieren fehlgeschlagen",
|
||||
"conversations_deleted": "Unterhaltungen gelöscht",
|
||||
"delete_conversations_confirm": "Diese Unterhaltungen löschen? Alle Nachrichten gehen verloren.",
|
||||
"delete_conversations_title": "Unterhaltungen löschen",
|
||||
"delete_message_confirm": "Diese Nachricht löschen? Das kann nicht rückgängig gemacht werden.",
|
||||
"retry_failed_confirm": "Alle {count} fehlgeschlagenen oder abgebrochenen Nachrichten erneut senden?",
|
||||
"send_oversized_confirm": "Diese Nachricht ist {size}. Empfänger mit dem Standardlimit von 900 KB können sie ablehnen. Trotzdem senden?",
|
||||
"failed_delete_conversations": "Unterhaltungen konnten nicht gelöscht werden",
|
||||
"failed_export_folders": "Ordner konnten nicht exportiert werden",
|
||||
"folders_imported": "Ordner importiert",
|
||||
|
|
@ -2271,7 +2285,10 @@
|
|||
"hide_nodes": "Knoten ausblenden",
|
||||
"show_snapshots": "Schnappschüsse anzeigen",
|
||||
"hide_snapshots": "Schnappschüsse ausblenden",
|
||||
"delete_snapshot": "Schnappschuss löschen"
|
||||
"delete_snapshot": "Schnappschuss löschen",
|
||||
"delete_selected_confirm": "{count} ausgewählte Schnappschüsse löschen?",
|
||||
"delete_snapshot_confirm": "Diesen Schnappschuss löschen?",
|
||||
"failed_delete": "Schnappschuss konnte nicht gelöscht werden. Bitte erneut versuchen."
|
||||
},
|
||||
"docs": {
|
||||
"title": "Dokumentation",
|
||||
|
|
@ -3458,6 +3475,25 @@
|
|||
"failed_load_audio_edit": "Fehler beim Laden des Audios zur Bearbeitung",
|
||||
"ringtone_saved": "Klingelton erfolgreich gespeichert",
|
||||
"failed_save_ringtone": "Fehler beim Speichern des bearbeiteten Klingeltons",
|
||||
"lxst_disabled_title": "LXST ist deaktiviert",
|
||||
"lxst_disabled_body": "Telefonie ist derzeit deaktiviert. Aktivieren Sie sie, um Anrufe zu tätigen und entgegenzunehmen.",
|
||||
"enable_lxst": "LXST aktivieren",
|
||||
"identity_or_name": "Identitätshash oder Name",
|
||||
"search_recordings": "Aufnahmen suchen...",
|
||||
"search_voicemails": "Voicemails suchen...",
|
||||
"banish_identity_confirm": "Diese Identität verbannen?",
|
||||
"delete_contact_confirm": "Diesen Kontakt löschen?",
|
||||
"delete_greeting_confirm": "Die eigene Begrüßung löschen?",
|
||||
"delete_recording_confirm": "Diese Aufnahme löschen?",
|
||||
"recording_enabled": "Anrufaufzeichnung aktiviert",
|
||||
"recording_disabled": "Anrufaufzeichnung deaktiviert",
|
||||
"failed_to_save_contact": "Kontakt konnte nicht gespeichert werden",
|
||||
"failed_to_upload_greeting": "Begrüßung konnte nicht hochgeladen werden",
|
||||
"telephony_enabled": "Telefonie aktiviert",
|
||||
"telephony_disabled": "Telefonie deaktiviert",
|
||||
"no_contacts_hint": "Fügen Sie Kontakte hinzu, um sie schnell anzurufen.",
|
||||
"custom": "Benutzerdefiniert",
|
||||
"custom_ringtone_set": "Eigener Klingelton gesetzt",
|
||||
"codec2_unavailable": "Codec2 is not available on this device. Low-bandwidth call profiles are hidden.",
|
||||
"codec2_profile_remapped": "Codec2 is unavailable so the call profile was switched to Opus.",
|
||||
"microphone_permission_needed": "Erlauben Sie den Mikrofonzugriff, wenn der Browser danach fragt, und klicken Sie danach erneut auf Geräte aktualisieren.",
|
||||
|
|
@ -3703,6 +3739,14 @@
|
|||
"shortcut_saved": "Verknüpfung gespeichert",
|
||||
"shortcut_deleted": "Verknüpfung gelöscht",
|
||||
"archived_pages_flushed": "Archivierte Seiten geleert.",
|
||||
"flush_archived_pages_confirm": "Alle archivierten Seiten löschen? Das kann nicht rückgängig gemacht werden.",
|
||||
"outbound_bubble_color": "Ausgehende Farbe",
|
||||
"failed_bubble_color": "Fehlerfarbe",
|
||||
"waiting_bubble_color": "Wartefarbe",
|
||||
"inbound_bubble_color": "Eingehende Farbe (optional)",
|
||||
"inbound_bubble_reset": "Auf Standard zurücksetzen",
|
||||
"inbound_bubble_customize": "Anpassen",
|
||||
"inbound_bubble_default_hint": "Es gilt die Standardfarbe des Themas.",
|
||||
"failed_enable_transport": "Transportmodus aktivieren fehlgeschlagen!",
|
||||
"failed_disable_transport": "Transportmodus deaktivieren fehlgeschlagen!",
|
||||
"failed_reload_reticulum": "Reticulum neu laden fehlgeschlagen!",
|
||||
|
|
|
|||
|
|
@ -596,7 +596,9 @@
|
|||
"loading": "Loading...",
|
||||
"ok": "OK",
|
||||
"clear": "Clear",
|
||||
"prompt_title": "Enter a value"
|
||||
"prompt_title": "Enter a value",
|
||||
"optional": "Optional",
|
||||
"back": "Back"
|
||||
},
|
||||
"stickers": {
|
||||
"settings_title": "Stickers",
|
||||
|
|
@ -1542,6 +1544,12 @@
|
|||
"source_updated": "Map source updated",
|
||||
"failed_set_active": "Failed to set active map",
|
||||
"file_deleted": "File deleted",
|
||||
"delete_file_confirm": "Delete {name}?",
|
||||
"clear_drawings_confirm": "Clear all drawings from the map?",
|
||||
"delete_drawing_confirm": "Delete drawing \"{name}\"?",
|
||||
"tracking_enabled": "Live tracking enabled",
|
||||
"tracking_disabled": "Live tracking disabled",
|
||||
"failed_update_tracking": "Failed to update tracking status",
|
||||
"failed_delete_file": "Failed to delete file",
|
||||
"storage_saved": "Storage directory saved",
|
||||
"failed_save_storage": "Failed to save directory",
|
||||
|
|
@ -1872,6 +1880,11 @@
|
|||
"marked_all_read": "All conversations marked as read",
|
||||
"failed_mark_read": "Failed to mark as read",
|
||||
"conversations_deleted": "Conversations deleted",
|
||||
"delete_conversations_confirm": "Delete these conversations? All messages will be lost.",
|
||||
"delete_conversations_title": "Delete conversations",
|
||||
"delete_message_confirm": "Delete this message? This cannot be undone.",
|
||||
"retry_failed_confirm": "Retry sending all {count} failed or cancelled messages?",
|
||||
"send_oversized_confirm": "This message is {size}. Recipients with the default 900 KB delivery limit may reject it. Send anyway?",
|
||||
"failed_delete_conversations": "Failed to delete conversations",
|
||||
"failed_export_folders": "Failed to export folders",
|
||||
"folders_imported": "Folders imported",
|
||||
|
|
@ -2061,6 +2074,14 @@
|
|||
"keyboard_shortcuts_title": "Keyboard Shortcuts",
|
||||
"keyboard_shortcuts_description": "Customize quick keyboard actions. Collapsed by default on phones.",
|
||||
"archived_pages_flushed": "Archived pages flushed.",
|
||||
"flush_archived_pages_confirm": "Delete all archived pages? This cannot be undone.",
|
||||
"outbound_bubble_color": "Outbound color",
|
||||
"failed_bubble_color": "Failed color",
|
||||
"waiting_bubble_color": "Waiting color",
|
||||
"inbound_bubble_color": "Inbound color (optional)",
|
||||
"inbound_bubble_reset": "Reset to default",
|
||||
"inbound_bubble_customize": "Customize",
|
||||
"inbound_bubble_default_hint": "Using the theme default.",
|
||||
"failed_enable_transport": "Failed to enable transport mode!",
|
||||
"failed_disable_transport": "Failed to disable transport mode!",
|
||||
"failed_update_reticulum_instance": "Failed to update Reticulum instance settings!",
|
||||
|
|
@ -2488,7 +2509,10 @@
|
|||
"hide_nodes": "Hide nodes",
|
||||
"show_snapshots": "Show snapshots",
|
||||
"hide_snapshots": "Hide snapshots",
|
||||
"delete_snapshot": "Delete snapshot"
|
||||
"delete_snapshot": "Delete snapshot",
|
||||
"delete_selected_confirm": "Delete {count} selected snapshots?",
|
||||
"delete_snapshot_confirm": "Delete this snapshot?",
|
||||
"failed_delete": "Failed to delete snapshot. Try again."
|
||||
},
|
||||
"docs": {
|
||||
"title": "Documentation",
|
||||
|
|
@ -4067,7 +4091,26 @@
|
|||
"failed_to_send_to_voicemail": "Failed to send call to voicemail",
|
||||
"failed_load_audio_edit": "Failed to load audio for editing",
|
||||
"ringtone_saved": "Ringtone saved successfully",
|
||||
"failed_save_ringtone": "Failed to save edited ringtone"
|
||||
"failed_save_ringtone": "Failed to save edited ringtone",
|
||||
"lxst_disabled_title": "LXST is disabled",
|
||||
"lxst_disabled_body": "Telephony is currently disabled. Enable it to make and receive calls.",
|
||||
"enable_lxst": "Enable LXST",
|
||||
"identity_or_name": "Identity hash or name",
|
||||
"search_recordings": "Search recordings...",
|
||||
"search_voicemails": "Search voicemails...",
|
||||
"banish_identity_confirm": "Banish this identity?",
|
||||
"delete_contact_confirm": "Delete this contact?",
|
||||
"delete_greeting_confirm": "Delete your custom greeting?",
|
||||
"delete_recording_confirm": "Delete this recording?",
|
||||
"recording_enabled": "Call recording enabled",
|
||||
"recording_disabled": "Call recording disabled",
|
||||
"failed_to_save_contact": "Failed to save contact",
|
||||
"failed_to_upload_greeting": "Failed to upload greeting",
|
||||
"telephony_enabled": "Telephony enabled",
|
||||
"telephony_disabled": "Telephony disabled",
|
||||
"no_contacts_hint": "Add contacts to call them quickly.",
|
||||
"custom": "Custom",
|
||||
"custom_ringtone_set": "Custom ringtone set"
|
||||
},
|
||||
"contacts": {
|
||||
"title": "Contacts",
|
||||
|
|
@ -4090,6 +4133,7 @@
|
|||
"failed_add_contact": "Failed to add contact",
|
||||
"remove_contact": "Remove Contact",
|
||||
"remove_contact_confirm": "Remove this contact?",
|
||||
"remove_duplicates_confirm": "Remove this contact?\n\n{count} additional duplicate(s) named \"{name}\" will also be removed.",
|
||||
"contact_removed": "Contact removed",
|
||||
"failed_remove_contact": "Failed to remove contact",
|
||||
"share_contact": "Share Contact URI",
|
||||
|
|
|
|||
|
|
@ -596,7 +596,9 @@
|
|||
"loading": "Cargando...",
|
||||
"ok": "Aceptar",
|
||||
"clear": "Limpiar",
|
||||
"prompt_title": "Introduce un valor"
|
||||
"prompt_title": "Introduce un valor",
|
||||
"optional": "Opcional",
|
||||
"back": "Atrás"
|
||||
},
|
||||
"stickers": {
|
||||
"settings_title": "Pegatinas",
|
||||
|
|
@ -1542,6 +1544,12 @@
|
|||
"source_updated": "Fuente de mapa actualizada",
|
||||
"failed_set_active": "No se pudo establecer el mapa activo",
|
||||
"file_deleted": "Archivo eliminado",
|
||||
"delete_file_confirm": "¿Eliminar {name}?",
|
||||
"clear_drawings_confirm": "¿Borrar todos los dibujos del mapa?",
|
||||
"delete_drawing_confirm": "¿Eliminar el dibujo \"{name}\"?",
|
||||
"tracking_enabled": "Seguimiento en vivo activado",
|
||||
"tracking_disabled": "Seguimiento en vivo desactivado",
|
||||
"failed_update_tracking": "No se pudo actualizar el seguimiento",
|
||||
"failed_delete_file": "Error al eliminar archivo",
|
||||
"storage_saved": "Directorio de almacenamiento guardado",
|
||||
"failed_save_storage": "No se pudo guardar el directorio",
|
||||
|
|
@ -1872,6 +1880,11 @@
|
|||
"marked_all_read": "Todas las conversaciones marcadas como leídas",
|
||||
"failed_mark_read": "Error al marcar como leído",
|
||||
"conversations_deleted": "Conversaciones eliminadas",
|
||||
"delete_conversations_confirm": "¿Eliminar estas conversaciones? Se perderán todos los mensajes.",
|
||||
"delete_conversations_title": "Eliminar conversaciones",
|
||||
"delete_message_confirm": "¿Eliminar este mensaje? Esto no se puede deshacer.",
|
||||
"retry_failed_confirm": "¿Reenviar los {count} mensajes fallidos o cancelados?",
|
||||
"send_oversized_confirm": "Este mensaje ocupa {size}. Los destinatarios con el límite predeterminado de 900 KB pueden rechazarlo. ¿Enviar de todos modos?",
|
||||
"failed_delete_conversations": "Fallado para borrar conversaciones",
|
||||
"failed_export_folders": "Error al exportar carpetas",
|
||||
"folders_imported": "Carpetas importadas",
|
||||
|
|
@ -2037,6 +2050,14 @@
|
|||
"shortcut_saved": "Guardado a mano",
|
||||
"shortcut_deleted": "Atajo eliminado",
|
||||
"archived_pages_flushed": "Las páginas archivadas se desplomaron.",
|
||||
"flush_archived_pages_confirm": "¿Eliminar todas las páginas archivadas? Esto no se puede deshacer.",
|
||||
"outbound_bubble_color": "Color de salida",
|
||||
"failed_bubble_color": "Color de error",
|
||||
"waiting_bubble_color": "Color en espera",
|
||||
"inbound_bubble_color": "Color de entrada (opcional)",
|
||||
"inbound_bubble_reset": "Restablecer",
|
||||
"inbound_bubble_customize": "Personalizar",
|
||||
"inbound_bubble_default_hint": "Se usa el color del tema.",
|
||||
"failed_enable_transport": "¡Failed to enable transport mode!",
|
||||
"failed_disable_transport": "¡Failed to disable transport mode!",
|
||||
"failed_reload_reticulum": "¡Failed to reload Reticulum!",
|
||||
|
|
@ -2488,7 +2509,10 @@
|
|||
"hide_nodes": "Ocultar nodos",
|
||||
"show_snapshots": "Mostrar instantáneas",
|
||||
"hide_snapshots": "Ocultar instantáneas",
|
||||
"delete_snapshot": "Eliminar instantánea"
|
||||
"delete_snapshot": "Eliminar instantánea",
|
||||
"delete_selected_confirm": "¿Eliminar {count} instantáneas seleccionadas?",
|
||||
"delete_snapshot_confirm": "¿Eliminar esta instantánea?",
|
||||
"failed_delete": "No se pudo eliminar la instantánea. Inténtalo de nuevo."
|
||||
},
|
||||
"docs": {
|
||||
"title": "Documentación",
|
||||
|
|
@ -3675,6 +3699,25 @@
|
|||
"failed_load_audio_edit": "No se puede cargar audio para editar",
|
||||
"ringtone_saved": "Ringtone se salvó con éxito",
|
||||
"failed_save_ringtone": "Error al guardar el tono editado",
|
||||
"lxst_disabled_title": "LXST está desactivado",
|
||||
"lxst_disabled_body": "La telefonía está desactivada. Actívala para hacer y recibir llamadas.",
|
||||
"enable_lxst": "Activar LXST",
|
||||
"identity_or_name": "Hash de identidad o nombre",
|
||||
"search_recordings": "Buscar grabaciones...",
|
||||
"search_voicemails": "Buscar buzón de voz...",
|
||||
"banish_identity_confirm": "¿Desterrar esta identidad?",
|
||||
"delete_contact_confirm": "¿Eliminar este contacto?",
|
||||
"delete_greeting_confirm": "¿Eliminar tu saludo personalizado?",
|
||||
"delete_recording_confirm": "¿Eliminar esta grabación?",
|
||||
"recording_enabled": "Grabación de llamadas activada",
|
||||
"recording_disabled": "Grabación de llamadas desactivada",
|
||||
"failed_to_save_contact": "No se pudo guardar el contacto",
|
||||
"failed_to_upload_greeting": "No se pudo subir el saludo",
|
||||
"telephony_enabled": "Telefonía activada",
|
||||
"telephony_disabled": "Telefonía desactivada",
|
||||
"no_contacts_hint": "Añade contactos para llamarlos rápido.",
|
||||
"custom": "Personalizado",
|
||||
"custom_ringtone_set": "Tono personalizado configurado",
|
||||
"codec2_unavailable": "Codec2 is not available on this device. Low-bandwidth call profiles are hidden.",
|
||||
"codec2_profile_remapped": "Codec2 is unavailable so the call profile was switched to Opus.",
|
||||
"microphone_permission_needed": "Permita el acceso al micrófono cuando el navegador lo solicite y luego pulse Actualizar dispositivos de nuevo.",
|
||||
|
|
@ -3701,6 +3744,7 @@
|
|||
"failed_add_contact": "No se pudo agregar el contacto",
|
||||
"remove_contact": "Quitar contacto",
|
||||
"remove_contact_confirm": "¿Retirar este contacto?",
|
||||
"remove_duplicates_confirm": "¿Quitar este contacto?\n\nTambién se quitarán {count} duplicado(s) llamado(s) \"{name}\".",
|
||||
"contact_removed": "Contacto eliminado",
|
||||
"failed_remove_contact": "No se pudo eliminar el contacto",
|
||||
"share_contact": "Compartir contacto URI",
|
||||
|
|
|
|||
|
|
@ -596,7 +596,9 @@
|
|||
"loading": "Ladataan...",
|
||||
"ok": "OK",
|
||||
"clear": "Tyhjää",
|
||||
"prompt_title": "Anna arvo"
|
||||
"prompt_title": "Anna arvo",
|
||||
"optional": "Valinnainen",
|
||||
"back": "Takaisin"
|
||||
},
|
||||
"stickers": {
|
||||
"settings_title": "Tarrat",
|
||||
|
|
@ -1542,6 +1544,12 @@
|
|||
"source_updated": "Kartan lähde päivitetty",
|
||||
"failed_set_active": "Aktiivisen kartan asetus epäonnistui",
|
||||
"file_deleted": "Tiedosto poistettu",
|
||||
"delete_file_confirm": "Poistetaanko {name}?",
|
||||
"clear_drawings_confirm": "Poistetaanko kaikki piirrokset kartalta?",
|
||||
"delete_drawing_confirm": "Poistetaanko piirros \"{name}\"?",
|
||||
"tracking_enabled": "Live-seuranta käytössä",
|
||||
"tracking_disabled": "Live-seuranta pois käytöstä",
|
||||
"failed_update_tracking": "Seurantatilan päivitys epäonnistui",
|
||||
"failed_delete_file": "Tiedoston poisto epäonnistui",
|
||||
"storage_saved": "Tallennushakemisto asetettu",
|
||||
"failed_save_storage": "Hakemiston asetus epäonnistui",
|
||||
|
|
@ -1872,6 +1880,11 @@
|
|||
"marked_all_read": "Kaikki keskustelut merkitty luetuiksi",
|
||||
"failed_mark_read": "Luetuksi merkitseminen epäonnistui",
|
||||
"conversations_deleted": "Keskustelut poistettu",
|
||||
"delete_conversations_confirm": "Poistetaanko nämä keskustelut? Kaikki viestit katoavat.",
|
||||
"delete_conversations_title": "Poista keskustelut",
|
||||
"delete_message_confirm": "Poistetaanko tämä viesti? Tätä ei voi perua.",
|
||||
"retry_failed_confirm": "Lähetetäänkö uudelleen kaikki {count} epäonnistunutta tai peruttua viestiä?",
|
||||
"send_oversized_confirm": "Tämä viesti on {size}. Vastaanottajat, joilla on oletusraja 900 kt, voivat hylätä sen. Lähetetäänkö silti?",
|
||||
"failed_delete_conversations": "Keskustelujen poisto epäonnistui",
|
||||
"failed_export_folders": "Kansioiden vienti epäonnistui",
|
||||
"folders_imported": "Kansiot tuotu",
|
||||
|
|
@ -2059,6 +2072,14 @@
|
|||
"shortcut_saved": "Näppäinoikotie tallennettu",
|
||||
"shortcut_deleted": "Näppäinoikotie poistettu",
|
||||
"archived_pages_flushed": "Arkisto puhdistettu.",
|
||||
"flush_archived_pages_confirm": "Poistetaanko kaikki arkistoidut sivut? Tätä ei voi perua.",
|
||||
"outbound_bubble_color": "Lähtevän väri",
|
||||
"failed_bubble_color": "Virheväri",
|
||||
"waiting_bubble_color": "Odotusväri",
|
||||
"inbound_bubble_color": "Saapuvan väri (valinnainen)",
|
||||
"inbound_bubble_reset": "Palauta oletus",
|
||||
"inbound_bubble_customize": "Muokkaa",
|
||||
"inbound_bubble_default_hint": "Käytössä teeman oletus.",
|
||||
"failed_enable_transport": "Välitystilan kytkeminen päälle epäonnistui!",
|
||||
"failed_disable_transport": "Välitystilan kytkeminen pois epäonnistui!",
|
||||
"failed_reload_reticulum": "Reticulumin lataus epäonnistui!",
|
||||
|
|
@ -2488,7 +2509,10 @@
|
|||
"hide_nodes": "Piilota solmut",
|
||||
"show_snapshots": "Näytä tilannevedokset",
|
||||
"hide_snapshots": "Piilota tilannevedokset",
|
||||
"delete_snapshot": "Poista tilannevedos"
|
||||
"delete_snapshot": "Poista tilannevedos",
|
||||
"delete_selected_confirm": "Poistetaanko {count} valittua tilannevedosta?",
|
||||
"delete_snapshot_confirm": "Poistetaanko tämä tilannevedos?",
|
||||
"failed_delete": "Tilannevedoksen poisto epäonnistui. Yritä uudelleen."
|
||||
},
|
||||
"docs": {
|
||||
"title": "Dokumentaatio",
|
||||
|
|
@ -3883,6 +3907,25 @@
|
|||
"failed_load_audio_edit": "Äänen lataaminen muokkausta varten epäonnistui",
|
||||
"ringtone_saved": "Soittoääni tallennettu onnistuneesti",
|
||||
"failed_save_ringtone": "Muokatun soittoäänen tallentaminen epäonnistui",
|
||||
"lxst_disabled_title": "LXST on pois käytöstä",
|
||||
"lxst_disabled_body": "Puhelut ovat pois käytöstä. Ota ne käyttöön soittaaksesi ja vastaanottaaksesi.",
|
||||
"enable_lxst": "Ota LXST käyttöön",
|
||||
"identity_or_name": "Identiteetin tiiviste tai nimi",
|
||||
"search_recordings": "Hae nauhoituksia...",
|
||||
"search_voicemails": "Hae vastaajaviestejä...",
|
||||
"banish_identity_confirm": "Karkotetaanko tämä identiteetti?",
|
||||
"delete_contact_confirm": "Poistetaanko tämä yhteystieto?",
|
||||
"delete_greeting_confirm": "Poistetaanko mukautettu tervehdys?",
|
||||
"delete_recording_confirm": "Poistetaanko tämä nauhoitus?",
|
||||
"recording_enabled": "Puhelun nauhoitus käytössä",
|
||||
"recording_disabled": "Puhelun nauhoitus pois käytöstä",
|
||||
"failed_to_save_contact": "Yhteystiedon tallennus epäonnistui",
|
||||
"failed_to_upload_greeting": "Tervehdyksen lataus epäonnistui",
|
||||
"telephony_enabled": "Puhelut käytössä",
|
||||
"telephony_disabled": "Puhelut pois käytöstä",
|
||||
"no_contacts_hint": "Lisää yhteystietoja, jotta voit soittaa heille nopeasti.",
|
||||
"custom": "Mukautettu",
|
||||
"custom_ringtone_set": "Mukautettu soittoääni asetettu",
|
||||
"codec2_unavailable": "Codec2 is not available on this device. Low-bandwidth call profiles are hidden.",
|
||||
"codec2_profile_remapped": "Codec2 is unavailable so the call profile was switched to Opus.",
|
||||
"microphone_permission_needed": "Salli mikrofonin käyttö, kun selain pyytää sitä, ja napsauta sitten Päivitä laitteet uudelleen.",
|
||||
|
|
@ -3909,6 +3952,7 @@
|
|||
"failed_add_contact": "Yhteyshenkilön lisääminen epäonnistui",
|
||||
"remove_contact": "Poista yhteystieto",
|
||||
"remove_contact_confirm": "Poistetaanko tämä yhteystieto?",
|
||||
"remove_duplicates_confirm": "Poistetaanko tämä yhteystieto?\n\nMyös {count} muuta kaksoiskappaletta nimellä \"{name}\" poistetaan.",
|
||||
"contact_removed": "Yhteystieto poistettu",
|
||||
"failed_remove_contact": "Yhteystiedon poisto epäonnistui",
|
||||
"share_contact": "Jaa yhteystiedon URI",
|
||||
|
|
|
|||
|
|
@ -596,7 +596,9 @@
|
|||
"loading": "Chargement...",
|
||||
"ok": "Très bien.",
|
||||
"clear": "Effacer",
|
||||
"prompt_title": "Saisir une valeur"
|
||||
"prompt_title": "Saisir une valeur",
|
||||
"optional": "Facultatif",
|
||||
"back": "Retour"
|
||||
},
|
||||
"stickers": {
|
||||
"settings_title": "Autocollants",
|
||||
|
|
@ -1542,6 +1544,12 @@
|
|||
"source_updated": "Source de la carte mise à jour",
|
||||
"failed_set_active": "Impossible de définir la carte active",
|
||||
"file_deleted": "Fichier supprimé",
|
||||
"delete_file_confirm": "Supprimer {name} ?",
|
||||
"clear_drawings_confirm": "Effacer tous les dessins de la carte ?",
|
||||
"delete_drawing_confirm": "Supprimer le dessin \"{name}\" ?",
|
||||
"tracking_enabled": "Suivi en direct activé",
|
||||
"tracking_disabled": "Suivi en direct désactivé",
|
||||
"failed_update_tracking": "Impossible de mettre à jour le suivi",
|
||||
"failed_delete_file": "Impossible de supprimer le fichier",
|
||||
"storage_saved": "Répertoire de stockage enregistré",
|
||||
"failed_save_storage": "Impossible d'enregistrer le répertoire",
|
||||
|
|
@ -1872,6 +1880,11 @@
|
|||
"marked_all_read": "Toutes les conversations marquées comme lues",
|
||||
"failed_mark_read": "Échec du marquage comme suit:",
|
||||
"conversations_deleted": "Conversations supprimées",
|
||||
"delete_conversations_confirm": "Supprimer ces conversations ? Tous les messages seront perdus.",
|
||||
"delete_conversations_title": "Supprimer les conversations",
|
||||
"delete_message_confirm": "Supprimer ce message ? Cette action est irréversible.",
|
||||
"retry_failed_confirm": "Renvoyer les {count} messages échoués ou annulés ?",
|
||||
"send_oversized_confirm": "Ce message fait {size}. Les destinataires avec la limite par défaut de 900 Ko peuvent le refuser. Envoyer quand même ?",
|
||||
"failed_delete_conversations": "Impossible de supprimer les conversations",
|
||||
"failed_export_folders": "Impossible d'exporter les dossiers",
|
||||
"folders_imported": "Dossiers importés",
|
||||
|
|
@ -2037,6 +2050,14 @@
|
|||
"shortcut_saved": "Raccourci enregistré",
|
||||
"shortcut_deleted": "Raccourci supprimé",
|
||||
"archived_pages_flushed": "Pages archivées bouffées.",
|
||||
"flush_archived_pages_confirm": "Supprimer toutes les pages archivées ? Cette action est irréversible.",
|
||||
"outbound_bubble_color": "Couleur sortante",
|
||||
"failed_bubble_color": "Couleur d'échec",
|
||||
"waiting_bubble_color": "Couleur d'attente",
|
||||
"inbound_bubble_color": "Couleur entrante (facultatif)",
|
||||
"inbound_bubble_reset": "Réinitialiser",
|
||||
"inbound_bubble_customize": "Personnaliser",
|
||||
"inbound_bubble_default_hint": "Couleur du thème par défaut.",
|
||||
"failed_enable_transport": "Impossible d'activer le mode de transport !",
|
||||
"failed_disable_transport": "Impossible de désactiver le mode de transport !",
|
||||
"failed_reload_reticulum": "Impossible de recharger Reticulum !",
|
||||
|
|
@ -2488,7 +2509,10 @@
|
|||
"hide_nodes": "Masquer les nœuds",
|
||||
"show_snapshots": "Afficher les instantanés",
|
||||
"hide_snapshots": "Masquer les instantanés",
|
||||
"delete_snapshot": "Supprimer l'instantané"
|
||||
"delete_snapshot": "Supprimer l'instantané",
|
||||
"delete_selected_confirm": "Supprimer {count} instantanés sélectionnés ?",
|
||||
"delete_snapshot_confirm": "Supprimer cet instantané ?",
|
||||
"failed_delete": "Impossible de supprimer l'instantané. Réessaie."
|
||||
},
|
||||
"docs": {
|
||||
"title": "Documentation",
|
||||
|
|
@ -3675,6 +3699,25 @@
|
|||
"failed_load_audio_edit": "Impossible de charger l'audio pour l'édition",
|
||||
"ringtone_saved": "Sonnerie enregistrée avec succès",
|
||||
"failed_save_ringtone": "Impossible d'enregistrer la sonnerie éditée",
|
||||
"lxst_disabled_title": "LXST est désactivé",
|
||||
"lxst_disabled_body": "La téléphonie est désactivée. Active-la pour passer et recevoir des appels.",
|
||||
"enable_lxst": "Activer LXST",
|
||||
"identity_or_name": "Hash d'identité ou nom",
|
||||
"search_recordings": "Rechercher des enregistrements...",
|
||||
"search_voicemails": "Rechercher des messages vocaux...",
|
||||
"banish_identity_confirm": "Bannir cette identité ?",
|
||||
"delete_contact_confirm": "Supprimer ce contact ?",
|
||||
"delete_greeting_confirm": "Supprimer ton message d'accueil personnalisé ?",
|
||||
"delete_recording_confirm": "Supprimer cet enregistrement ?",
|
||||
"recording_enabled": "Enregistrement des appels activé",
|
||||
"recording_disabled": "Enregistrement des appels désactivé",
|
||||
"failed_to_save_contact": "Impossible d'enregistrer le contact",
|
||||
"failed_to_upload_greeting": "Impossible d'envoyer le message d'accueil",
|
||||
"telephony_enabled": "Téléphonie activée",
|
||||
"telephony_disabled": "Téléphonie désactivée",
|
||||
"no_contacts_hint": "Ajoute des contacts pour les appeler rapidement.",
|
||||
"custom": "Personnalisé",
|
||||
"custom_ringtone_set": "Sonnerie personnalisée définie",
|
||||
"codec2_unavailable": "Codec2 is not available on this device. Low-bandwidth call profiles are hidden.",
|
||||
"codec2_profile_remapped": "Codec2 is unavailable so the call profile was switched to Opus.",
|
||||
"microphone_permission_needed": "Autorisez l'accès au microphone lorsque le navigateur le demande, puis cliquez à nouveau sur Actualiser les périphériques.",
|
||||
|
|
@ -3701,6 +3744,7 @@
|
|||
"failed_add_contact": "Impossible d'ajouter le contact",
|
||||
"remove_contact": "Supprimer le contact",
|
||||
"remove_contact_confirm": "Enlever ce contact ?",
|
||||
"remove_duplicates_confirm": "Retirer ce contact ?\n\n{count} doublon(s) nommé(s) \"{name}\" seront aussi retirés.",
|
||||
"contact_removed": "Contact enlevé",
|
||||
"failed_remove_contact": "Impossible de supprimer le contact",
|
||||
"share_contact": "Partager Contacter URI",
|
||||
|
|
|
|||
|
|
@ -596,7 +596,9 @@
|
|||
"loading": "Caricamento...",
|
||||
"ok": "OK",
|
||||
"clear": "Cancella",
|
||||
"prompt_title": "Inserisci un valore"
|
||||
"prompt_title": "Inserisci un valore",
|
||||
"optional": "Facoltativo",
|
||||
"back": "Indietro"
|
||||
},
|
||||
"stickers": {
|
||||
"settings_title": "Sticker",
|
||||
|
|
@ -1095,6 +1097,7 @@
|
|||
"failed_add_contact": "Impossibile aggiungere il contatto",
|
||||
"remove_contact": "Rimuovi contatto",
|
||||
"remove_contact_confirm": "Rimuovere questo contatto?",
|
||||
"remove_duplicates_confirm": "Rimuovere questo contatto?\n\nVerranno rimossi anche {count} duplicat(i) di nome \"{name}\".",
|
||||
"contact_removed": "Contatto rimosso",
|
||||
"failed_remove_contact": "Impossibile rimuovere il contatto",
|
||||
"share_contact": "Condividi URI contatto",
|
||||
|
|
@ -1595,6 +1598,12 @@
|
|||
"source_updated": "Sorgente mappa aggiornata",
|
||||
"failed_set_active": "Impossibile impostare la mappa attiva",
|
||||
"file_deleted": "File eliminato",
|
||||
"delete_file_confirm": "Eliminare {name}?",
|
||||
"clear_drawings_confirm": "Cancellare tutti i disegni dalla mappa?",
|
||||
"delete_drawing_confirm": "Eliminare il disegno \"{name}\"?",
|
||||
"tracking_enabled": "Tracciamento live attivato",
|
||||
"tracking_disabled": "Tracciamento live disattivato",
|
||||
"failed_update_tracking": "Impossibile aggiornare il tracciamento",
|
||||
"failed_delete_file": "Impossibile eliminare il file",
|
||||
"storage_saved": "Directory di archiviazione salvata",
|
||||
"failed_save_storage": "Impossibile salvare la directory",
|
||||
|
|
@ -1929,6 +1938,11 @@
|
|||
"marked_all_read": "Tutte le conversazioni segnate come lette",
|
||||
"failed_mark_read": "Impossibile segnare come letto",
|
||||
"conversations_deleted": "Conversazioni eliminate",
|
||||
"delete_conversations_confirm": "Eliminare queste conversazioni? Tutti i messaggi andranno persi.",
|
||||
"delete_conversations_title": "Elimina conversazioni",
|
||||
"delete_message_confirm": "Eliminare questo messaggio? L'azione non si può annullare.",
|
||||
"retry_failed_confirm": "Reinviare tutti i {count} messaggi non riusciti o annullati?",
|
||||
"send_oversized_confirm": "Questo messaggio è {size}. I destinatari con il limite predefinito di 900 KB potrebbero rifiutarlo. Inviare comunque?",
|
||||
"failed_delete_conversations": "Impossibile eliminare le conversazioni",
|
||||
"failed_export_folders": "Impossibile esportare le cartelle",
|
||||
"folders_imported": "Cartelle importate",
|
||||
|
|
@ -2090,6 +2104,14 @@
|
|||
"shortcut_saved": "Scorciatoia salvata",
|
||||
"shortcut_deleted": "Scorciatoia eliminata",
|
||||
"archived_pages_flushed": "Pagine archiviate svuotate.",
|
||||
"flush_archived_pages_confirm": "Eliminare tutte le pagine archiviate? L'azione non si può annullare.",
|
||||
"outbound_bubble_color": "Colore in uscita",
|
||||
"failed_bubble_color": "Colore errore",
|
||||
"waiting_bubble_color": "Colore in attesa",
|
||||
"inbound_bubble_color": "Colore in ingresso (facoltativo)",
|
||||
"inbound_bubble_reset": "Ripristina predefinito",
|
||||
"inbound_bubble_customize": "Personalizza",
|
||||
"inbound_bubble_default_hint": "Si usa il colore del tema.",
|
||||
"failed_enable_transport": "Impossibile abilitare la modalità trasporto!",
|
||||
"failed_disable_transport": "Impossibile disabilitare la modalità trasporto!",
|
||||
"failed_reload_reticulum": "Impossibile ricaricare Reticulum!",
|
||||
|
|
@ -2541,7 +2563,10 @@
|
|||
"hide_nodes": "Nascondi nodi",
|
||||
"show_snapshots": "Mostra snapshot",
|
||||
"hide_snapshots": "Nascondi snapshot",
|
||||
"delete_snapshot": "Elimina snapshot"
|
||||
"delete_snapshot": "Elimina snapshot",
|
||||
"delete_selected_confirm": "Eliminare {count} snapshot selezionati?",
|
||||
"delete_snapshot_confirm": "Eliminare questo snapshot?",
|
||||
"failed_delete": "Impossibile eliminare lo snapshot. Riprova."
|
||||
},
|
||||
"docs": {
|
||||
"title": "Documentazione",
|
||||
|
|
@ -3728,6 +3753,25 @@
|
|||
"failed_load_audio_edit": "Impossibile caricare l'audio per la modifica",
|
||||
"ringtone_saved": "Suoneria salvata con successo",
|
||||
"failed_save_ringtone": "Impossibile salvare la suoneria modificata",
|
||||
"lxst_disabled_title": "LXST è disattivato",
|
||||
"lxst_disabled_body": "La telefonia è disattivata. Attivala per fare e ricevere chiamate.",
|
||||
"enable_lxst": "Attiva LXST",
|
||||
"identity_or_name": "Hash identità o nome",
|
||||
"search_recordings": "Cerca registrazioni...",
|
||||
"search_voicemails": "Cerca messaggi in segreteria...",
|
||||
"banish_identity_confirm": "Bandire questa identità?",
|
||||
"delete_contact_confirm": "Eliminare questo contatto?",
|
||||
"delete_greeting_confirm": "Eliminare il messaggio di benvenuto personalizzato?",
|
||||
"delete_recording_confirm": "Eliminare questa registrazione?",
|
||||
"recording_enabled": "Registrazione chiamate attivata",
|
||||
"recording_disabled": "Registrazione chiamate disattivata",
|
||||
"failed_to_save_contact": "Impossibile salvare il contatto",
|
||||
"failed_to_upload_greeting": "Impossibile caricare il messaggio di benvenuto",
|
||||
"telephony_enabled": "Telefonia attivata",
|
||||
"telephony_disabled": "Telefonia disattivata",
|
||||
"no_contacts_hint": "Aggiungi contatti per chiamarli in fretta.",
|
||||
"custom": "Personalizzato",
|
||||
"custom_ringtone_set": "Suoneria personalizzata impostata",
|
||||
"codec2_unavailable": "Codec2 is not available on this device. Low-bandwidth call profiles are hidden.",
|
||||
"codec2_profile_remapped": "Codec2 is unavailable so the call profile was switched to Opus.",
|
||||
"microphone_permission_needed": "Consenti l'accesso al microfono quando il browser lo richiede, poi fai di nuovo clic su Aggiorna dispositivi.",
|
||||
|
|
|
|||
|
|
@ -596,7 +596,9 @@
|
|||
"loading": "Laden...",
|
||||
"ok": "OK",
|
||||
"clear": "Wissen",
|
||||
"prompt_title": "Voer een waarde in"
|
||||
"prompt_title": "Voer een waarde in",
|
||||
"optional": "Optioneel",
|
||||
"back": "Terug"
|
||||
},
|
||||
"stickers": {
|
||||
"settings_title": "Stickers",
|
||||
|
|
@ -1542,6 +1544,12 @@
|
|||
"source_updated": "Kaartbron bijgewerkt",
|
||||
"failed_set_active": "Kon actieve kaart niet instellen",
|
||||
"file_deleted": "Bestand verwijderd",
|
||||
"delete_file_confirm": "{name} verwijderen?",
|
||||
"clear_drawings_confirm": "Alle tekeningen van de kaart wissen?",
|
||||
"delete_drawing_confirm": "Tekening \"{name}\" verwijderen?",
|
||||
"tracking_enabled": "Live tracking ingeschakeld",
|
||||
"tracking_disabled": "Live tracking uitgeschakeld",
|
||||
"failed_update_tracking": "Trackingstatus bijwerken mislukt",
|
||||
"failed_delete_file": "Verwijderen van bestand mislukt",
|
||||
"storage_saved": "Opslagmap opgeslagen",
|
||||
"failed_save_storage": "Kon map niet opslaan",
|
||||
|
|
@ -1872,6 +1880,11 @@
|
|||
"marked_all_read": "Alle gesprekken gemarkeerd als gelezen",
|
||||
"failed_mark_read": "Markeren als gelezen is mislukt",
|
||||
"conversations_deleted": "Gesprekken verwijderd",
|
||||
"delete_conversations_confirm": "Deze gesprekken verwijderen? Alle berichten gaan verloren.",
|
||||
"delete_conversations_title": "Gesprekken verwijderen",
|
||||
"delete_message_confirm": "Dit bericht verwijderen? Dit kan niet ongedaan worden gemaakt.",
|
||||
"retry_failed_confirm": "Alle {count} mislukte of geannuleerde berichten opnieuw verzenden?",
|
||||
"send_oversized_confirm": "Dit bericht is {size}. Ontvangers met de standaardlimiet van 900 KB kunnen het weigeren. Toch verzenden?",
|
||||
"failed_delete_conversations": "Kon gesprekken niet verwijderen",
|
||||
"failed_export_folders": "Exporteren van mappen is mislukt",
|
||||
"folders_imported": "Mappen geïmporteerd",
|
||||
|
|
@ -2037,6 +2050,14 @@
|
|||
"shortcut_saved": "Sneltoets opgeslagen",
|
||||
"shortcut_deleted": "Sneltoets verwijderd",
|
||||
"archived_pages_flushed": "Gearchiveerde pagina's doorgespoeld.",
|
||||
"flush_archived_pages_confirm": "Alle gearchiveerde pagina's verwijderen? Dit kan niet ongedaan worden gemaakt.",
|
||||
"outbound_bubble_color": "Uitgaande kleur",
|
||||
"failed_bubble_color": "Foutkleur",
|
||||
"waiting_bubble_color": "Wachtkleur",
|
||||
"inbound_bubble_color": "Inkomende kleur (optioneel)",
|
||||
"inbound_bubble_reset": "Standaard herstellen",
|
||||
"inbound_bubble_customize": "Aanpassen",
|
||||
"inbound_bubble_default_hint": "Thema-standaardkleur wordt gebruikt.",
|
||||
"failed_enable_transport": "Kon transportmodus niet inschakelen!",
|
||||
"failed_disable_transport": "Kon transportmodus niet uitschakelen!",
|
||||
"failed_reload_reticulum": "Herladen van Reticulum is mislukt!",
|
||||
|
|
@ -2488,7 +2509,10 @@
|
|||
"hide_nodes": "Nodes verbergen",
|
||||
"show_snapshots": "Snapshots tonen",
|
||||
"hide_snapshots": "Snapshots verbergen",
|
||||
"delete_snapshot": "Snapshot verwijderen"
|
||||
"delete_snapshot": "Snapshot verwijderen",
|
||||
"delete_selected_confirm": "{count} geselecteerde snapshots verwijderen?",
|
||||
"delete_snapshot_confirm": "Deze snapshot verwijderen?",
|
||||
"failed_delete": "Snapshot verwijderen mislukt. Probeer het opnieuw."
|
||||
},
|
||||
"docs": {
|
||||
"title": "Documentatie",
|
||||
|
|
@ -3675,6 +3699,25 @@
|
|||
"failed_load_audio_edit": "Kon audio niet laden om te bewerken",
|
||||
"ringtone_saved": "Ringtone is succesvol opgeslagen",
|
||||
"failed_save_ringtone": "Opslaan van bewerkte ringtone mislukt",
|
||||
"lxst_disabled_title": "LXST is uitgeschakeld",
|
||||
"lxst_disabled_body": "Telefonie is uitgeschakeld. Schakel het in om te bellen en gebeld te worden.",
|
||||
"enable_lxst": "LXST inschakelen",
|
||||
"identity_or_name": "Identiteitshash of naam",
|
||||
"search_recordings": "Opnamen zoeken...",
|
||||
"search_voicemails": "Voicemails zoeken...",
|
||||
"banish_identity_confirm": "Deze identiteit verbannen?",
|
||||
"delete_contact_confirm": "Dit contact verwijderen?",
|
||||
"delete_greeting_confirm": "Je aangepaste begroeting verwijderen?",
|
||||
"delete_recording_confirm": "Deze opname verwijderen?",
|
||||
"recording_enabled": "Gespreksopname ingeschakeld",
|
||||
"recording_disabled": "Gespreksopname uitgeschakeld",
|
||||
"failed_to_save_contact": "Contact opslaan mislukt",
|
||||
"failed_to_upload_greeting": "Begroeting uploaden mislukt",
|
||||
"telephony_enabled": "Telefonie ingeschakeld",
|
||||
"telephony_disabled": "Telefonie uitgeschakeld",
|
||||
"no_contacts_hint": "Voeg contacten toe om ze snel te bellen.",
|
||||
"custom": "Aangepast",
|
||||
"custom_ringtone_set": "Aangepaste ringtone ingesteld",
|
||||
"codec2_unavailable": "Codec2 is not available on this device. Low-bandwidth call profiles are hidden.",
|
||||
"codec2_profile_remapped": "Codec2 is unavailable so the call profile was switched to Opus.",
|
||||
"microphone_permission_needed": "Sta microfoontoegang toe wanneer de browser daarom vraagt en klik daarna opnieuw op Apparaten vernieuwen.",
|
||||
|
|
@ -3701,6 +3744,7 @@
|
|||
"failed_add_contact": "Kon contact niet toevoegen",
|
||||
"remove_contact": "Contact verwijderen",
|
||||
"remove_contact_confirm": "Dit contact verwijderen?",
|
||||
"remove_duplicates_confirm": "Dit contact verwijderen?\n\nOok {count} extra duplicaat/duplicaten met de naam \"{name}\" worden verwijderd.",
|
||||
"contact_removed": "Contact verwijderd",
|
||||
"failed_remove_contact": "Kon contact niet verwijderen",
|
||||
"share_contact": "Contact-URI delen",
|
||||
|
|
|
|||
|
|
@ -596,7 +596,9 @@
|
|||
"copy": "Копировать",
|
||||
"copy_to_clipboard": "Копировать в буфер обмена",
|
||||
"clear": "Очистить",
|
||||
"prompt_title": "Введите значение"
|
||||
"prompt_title": "Введите значение",
|
||||
"optional": "Необязательно",
|
||||
"back": "Назад"
|
||||
},
|
||||
"stickers": {
|
||||
"settings_title": "Стикеры",
|
||||
|
|
@ -1095,6 +1097,7 @@
|
|||
"failed_add_contact": "Не удалось добавить контакт",
|
||||
"remove_contact": "Удалить контакт",
|
||||
"remove_contact_confirm": "Удалить этот контакт?",
|
||||
"remove_duplicates_confirm": "Удалить этот контакт?\n\nТакже будут удалены {count} дубликат(ов) с именем \"{name}\".",
|
||||
"contact_removed": "Контакт удален",
|
||||
"failed_remove_contact": "Не удалось удалить контакт",
|
||||
"share_contact": "Поделиться URI контакта",
|
||||
|
|
@ -1595,6 +1598,12 @@
|
|||
"source_updated": "Источник карты обновлён",
|
||||
"failed_set_active": "Не удалось установить активную карту",
|
||||
"file_deleted": "Файл удалён",
|
||||
"delete_file_confirm": "Удалить {name}?",
|
||||
"clear_drawings_confirm": "Удалить все рисунки с карты?",
|
||||
"delete_drawing_confirm": "Удалить рисунок \"{name}\"?",
|
||||
"tracking_enabled": "Живое отслеживание включено",
|
||||
"tracking_disabled": "Живое отслеживание выключено",
|
||||
"failed_update_tracking": "Не удалось обновить статус отслеживания",
|
||||
"failed_delete_file": "Не удалось удалить файл",
|
||||
"storage_saved": "Каталог хранения сохранён",
|
||||
"failed_save_storage": "Не удалось сохранить каталог",
|
||||
|
|
@ -1929,6 +1938,11 @@
|
|||
"marked_all_read": "Все переписки отмечены как прочитанные",
|
||||
"failed_mark_read": "Не удалось отметить как прочитанное",
|
||||
"conversations_deleted": "Разговоры удалены",
|
||||
"delete_conversations_confirm": "Удалить эти переписки? Все сообщения будут потеряны.",
|
||||
"delete_conversations_title": "Удалить переписки",
|
||||
"delete_message_confirm": "Удалить это сообщение? Это нельзя отменить.",
|
||||
"retry_failed_confirm": "Повторить отправку всех {count} неудачных или отменённых сообщений?",
|
||||
"send_oversized_confirm": "Размер сообщения {size}. Получатели с лимитом по умолчанию 900 КБ могут отклонить его. Отправить всё равно?",
|
||||
"failed_delete_conversations": "Не удалось удалить разговоры",
|
||||
"failed_export_folders": "Не удалось экспортировать папки",
|
||||
"folders_imported": "Папки импортированы",
|
||||
|
|
@ -2271,7 +2285,10 @@
|
|||
"hide_nodes": "Скрыть узлы",
|
||||
"show_snapshots": "Показать снимки",
|
||||
"hide_snapshots": "Скрыть снимки",
|
||||
"delete_snapshot": "Удалить снимок"
|
||||
"delete_snapshot": "Удалить снимок",
|
||||
"delete_selected_confirm": "Удалить {count} выбранных снимков?",
|
||||
"delete_snapshot_confirm": "Удалить этот снимок?",
|
||||
"failed_delete": "Не удалось удалить снимок. Попробуй ещё раз."
|
||||
},
|
||||
"docs": {
|
||||
"title": "Документация",
|
||||
|
|
@ -3458,6 +3475,25 @@
|
|||
"failed_load_audio_edit": "Не удалось загрузить аудио для редактирования",
|
||||
"ringtone_saved": "Рингтон успешно сохранён",
|
||||
"failed_save_ringtone": "Не удалось сохранить отредактированный рингтон",
|
||||
"lxst_disabled_title": "LXST выключен",
|
||||
"lxst_disabled_body": "Телефония выключена. Включи её, чтобы звонить и принимать звонки.",
|
||||
"enable_lxst": "Включить LXST",
|
||||
"identity_or_name": "Хеш личности или имя",
|
||||
"search_recordings": "Поиск записей...",
|
||||
"search_voicemails": "Поиск голосовой почты...",
|
||||
"banish_identity_confirm": "Изгнать эту личность?",
|
||||
"delete_contact_confirm": "Удалить этот контакт?",
|
||||
"delete_greeting_confirm": "Удалить своё приветствие?",
|
||||
"delete_recording_confirm": "Удалить эту запись?",
|
||||
"recording_enabled": "Запись звонков включена",
|
||||
"recording_disabled": "Запись звонков выключена",
|
||||
"failed_to_save_contact": "Не удалось сохранить контакт",
|
||||
"failed_to_upload_greeting": "Не удалось загрузить приветствие",
|
||||
"telephony_enabled": "Телефония включена",
|
||||
"telephony_disabled": "Телефония выключена",
|
||||
"no_contacts_hint": "Добавь контакты, чтобы быстро им звонить.",
|
||||
"custom": "Свой",
|
||||
"custom_ringtone_set": "Задан свой рингтон",
|
||||
"codec2_unavailable": "Codec2 is not available on this device. Low-bandwidth call profiles are hidden.",
|
||||
"codec2_profile_remapped": "Codec2 is unavailable so the call profile was switched to Opus.",
|
||||
"microphone_permission_needed": "Разрешите доступ к микрофону, когда браузер запросит его, затем снова нажмите Обновить устройства.",
|
||||
|
|
@ -3703,6 +3739,14 @@
|
|||
"shortcut_saved": "Ярлык сохранён",
|
||||
"shortcut_deleted": "Ярлык удалён",
|
||||
"archived_pages_flushed": "Архивные страницы очищены.",
|
||||
"flush_archived_pages_confirm": "Удалить все архивные страницы? Это нельзя отменить.",
|
||||
"outbound_bubble_color": "Цвет исходящих",
|
||||
"failed_bubble_color": "Цвет ошибки",
|
||||
"waiting_bubble_color": "Цвет ожидания",
|
||||
"inbound_bubble_color": "Цвет входящих (необязательно)",
|
||||
"inbound_bubble_reset": "Сбросить",
|
||||
"inbound_bubble_customize": "Настроить",
|
||||
"inbound_bubble_default_hint": "Используется цвет темы.",
|
||||
"failed_enable_transport": "Не удалось включить режим транспорта!",
|
||||
"failed_disable_transport": "Не удалось отключить транспортный режим!",
|
||||
"failed_reload_reticulum": "Не удалось перезагрузить Reticulum!",
|
||||
|
|
|
|||
|
|
@ -596,7 +596,9 @@
|
|||
"loading": "正在加载...",
|
||||
"ok": "确定",
|
||||
"clear": "清除",
|
||||
"prompt_title": "输入值"
|
||||
"prompt_title": "输入值",
|
||||
"optional": "可选",
|
||||
"back": "返回"
|
||||
},
|
||||
"stickers": {
|
||||
"settings_title": "贴纸",
|
||||
|
|
@ -1542,6 +1544,12 @@
|
|||
"source_updated": "地图源已更新",
|
||||
"failed_set_active": "设置活动地图失败",
|
||||
"file_deleted": "文件已删除",
|
||||
"delete_file_confirm": "删除 {name}?",
|
||||
"clear_drawings_confirm": "清除地图上的所有绘图?",
|
||||
"delete_drawing_confirm": "删除绘图 \"{name}\"?",
|
||||
"tracking_enabled": "已启用实时跟踪",
|
||||
"tracking_disabled": "已关闭实时跟踪",
|
||||
"failed_update_tracking": "无法更新跟踪状态",
|
||||
"failed_delete_file": "删除文件失败",
|
||||
"storage_saved": "存储目录已保存",
|
||||
"failed_save_storage": "保存目录失败",
|
||||
|
|
@ -1872,6 +1880,11 @@
|
|||
"marked_all_read": "已将全部对话标记为已读",
|
||||
"failed_mark_read": "标记为已读失败",
|
||||
"conversations_deleted": "对话已删除",
|
||||
"delete_conversations_confirm": "删除这些会话?所有消息都会丢失。",
|
||||
"delete_conversations_title": "删除会话",
|
||||
"delete_message_confirm": "删除这条消息?此操作无法撤销。",
|
||||
"retry_failed_confirm": "重新发送全部 {count} 条失败或已取消的消息?",
|
||||
"send_oversized_confirm": "这条消息大小为 {size}。使用默认 900 KB 投递上限的对端可能会拒绝。仍要发送吗?",
|
||||
"failed_delete_conversations": "删除对话失败",
|
||||
"failed_export_folders": "导出文件夹失败",
|
||||
"folders_imported": "文件夹已导入",
|
||||
|
|
@ -2037,6 +2050,14 @@
|
|||
"shortcut_saved": "快捷键已保存",
|
||||
"shortcut_deleted": "快捷键已删除",
|
||||
"archived_pages_flushed": "已冲出存档页面 。",
|
||||
"flush_archived_pages_confirm": "删除所有已归档页面?此操作无法撤销。",
|
||||
"outbound_bubble_color": "发出气泡颜色",
|
||||
"failed_bubble_color": "失败气泡颜色",
|
||||
"waiting_bubble_color": "等待气泡颜色",
|
||||
"inbound_bubble_color": "收到气泡颜色(可选)",
|
||||
"inbound_bubble_reset": "恢复默认",
|
||||
"inbound_bubble_customize": "自定义",
|
||||
"inbound_bubble_default_hint": "使用主题默认颜色。",
|
||||
"failed_enable_transport": "启用传输模式失败 !",
|
||||
"failed_disable_transport": "禁用运输模式失败 !",
|
||||
"failed_reload_reticulum": "重新装入 Reticulum 失败 !",
|
||||
|
|
@ -2488,7 +2509,10 @@
|
|||
"hide_nodes": "隐藏节点",
|
||||
"show_snapshots": "显示快照",
|
||||
"hide_snapshots": "隐藏快照",
|
||||
"delete_snapshot": "删除快照"
|
||||
"delete_snapshot": "删除快照",
|
||||
"delete_selected_confirm": "删除所选的 {count} 个快照?",
|
||||
"delete_snapshot_confirm": "删除此快照?",
|
||||
"failed_delete": "删除快照失败。请重试。"
|
||||
},
|
||||
"docs": {
|
||||
"title": "文档",
|
||||
|
|
@ -3675,6 +3699,25 @@
|
|||
"failed_load_audio_edit": "加载用于编辑的音频失败",
|
||||
"ringtone_saved": "铃声保存成功",
|
||||
"failed_save_ringtone": "保存编辑的铃声失败",
|
||||
"lxst_disabled_title": "LXST 已关闭",
|
||||
"lxst_disabled_body": "电话功能当前已关闭。启用后才能拨打和接听。",
|
||||
"enable_lxst": "启用 LXST",
|
||||
"identity_or_name": "身份哈希或名称",
|
||||
"search_recordings": "搜索录音...",
|
||||
"search_voicemails": "搜索语音留言...",
|
||||
"banish_identity_confirm": "放逐此身份?",
|
||||
"delete_contact_confirm": "删除此联系人?",
|
||||
"delete_greeting_confirm": "删除自定义问候语?",
|
||||
"delete_recording_confirm": "删除此录音?",
|
||||
"recording_enabled": "已启用通话录音",
|
||||
"recording_disabled": "已关闭通话录音",
|
||||
"failed_to_save_contact": "保存联系人失败",
|
||||
"failed_to_upload_greeting": "上传问候语失败",
|
||||
"telephony_enabled": "已启用电话",
|
||||
"telephony_disabled": "已关闭电话",
|
||||
"no_contacts_hint": "添加联系人以便快速呼叫。",
|
||||
"custom": "自定义",
|
||||
"custom_ringtone_set": "已设置自定义铃声",
|
||||
"codec2_unavailable": "Codec2 is not available on this device. Low-bandwidth call profiles are hidden.",
|
||||
"codec2_profile_remapped": "Codec2 is unavailable so the call profile was switched to Opus.",
|
||||
"microphone_permission_needed": "请在浏览器提示时允许麦克风访问,然后再次点击刷新设备。",
|
||||
|
|
@ -3701,6 +3744,7 @@
|
|||
"failed_add_contact": "添加联系人失败",
|
||||
"remove_contact": "移除联系人",
|
||||
"remove_contact_confirm": "移除联系人?",
|
||||
"remove_duplicates_confirm": "移除此联系人?\n\n另外 {count} 个名为 \"{name}\" 的重复项也会被移除。",
|
||||
"contact_removed": "联系人已移除",
|
||||
"failed_remove_contact": "移除联系人失败",
|
||||
"share_contact": "共享联系人 URI",
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ describe("ConfirmDialog UI", () => {
|
|||
expect(showFn).toBeDefined();
|
||||
showFn({ message: "Delete this item?", resolve: vi.fn() });
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(wrapper.vm.pendingConfirm).toEqual({ message: "Delete this item?" });
|
||||
expect(wrapper.vm.pendingConfirm).toEqual({ message: "Delete this item?", title: "" });
|
||||
expect(wrapper.text()).toContain("common.confirm_action");
|
||||
expect(wrapper.text()).toContain("Delete this item?");
|
||||
});
|
||||
|
|
@ -89,7 +89,35 @@ describe("ConfirmDialog UI", () => {
|
|||
showFn({ message: "Second?", resolve: second });
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(first).toHaveBeenCalledWith(false);
|
||||
expect(wrapper.vm.pendingConfirm).toEqual({ message: "Second?" });
|
||||
expect(wrapper.vm.pendingConfirm).toEqual({ message: "Second?", title: "" });
|
||||
expect(second).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows an optional title when provided", async () => {
|
||||
const wrapper = mountDialog();
|
||||
const showFn = GlobalEmitter.on.mock.calls.find((c) => c[0] === "confirm")?.[1];
|
||||
showFn({ message: "All messages will be lost.", title: "Delete conversations", resolve: vi.fn() });
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(wrapper.text()).toContain("Delete conversations");
|
||||
expect(wrapper.text()).not.toContain("common.confirm_action");
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it("cancels on Escape and confirms on Enter", async () => {
|
||||
const resolve = vi.fn();
|
||||
const wrapper = mountDialog();
|
||||
const showFn = GlobalEmitter.on.mock.calls.find((c) => c[0] === "confirm")?.[1];
|
||||
showFn({ message: "Sure?", resolve });
|
||||
await wrapper.vm.$nextTick();
|
||||
window.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" }));
|
||||
expect(resolve).toHaveBeenCalledWith(false);
|
||||
expect(wrapper.vm.pendingConfirm).toBeNull();
|
||||
|
||||
const resolveEnter = vi.fn();
|
||||
showFn({ message: "Sure again?", resolve: resolveEnter });
|
||||
await wrapper.vm.$nextTick();
|
||||
window.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter" }));
|
||||
expect(resolveEnter).toHaveBeenCalledWith(true);
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,6 +7,53 @@ vi.mock("../../meshchatx/src/frontend/js/GlobalEmitter", () => ({
|
|||
import DialogUtils from "../../meshchatx/src/frontend/js/DialogUtils.js";
|
||||
import GlobalEmitter from "../../meshchatx/src/frontend/js/GlobalEmitter";
|
||||
|
||||
describe("DialogUtils.confirm", () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(GlobalEmitter.emit).mockClear();
|
||||
delete window.electron;
|
||||
});
|
||||
|
||||
it("uses the in-app confirm dialog even when electron is present", async () => {
|
||||
window.electron = {
|
||||
confirm: vi.fn().mockResolvedValue(true),
|
||||
};
|
||||
const pending = DialogUtils.confirm("Delete this?");
|
||||
expect(window.electron.confirm).not.toHaveBeenCalled();
|
||||
expect(GlobalEmitter.emit).toHaveBeenCalledWith(
|
||||
"confirm",
|
||||
expect.objectContaining({
|
||||
message: "Delete this?",
|
||||
resolve: expect.any(Function),
|
||||
})
|
||||
);
|
||||
const payload = GlobalEmitter.emit.mock.calls.find((c) => c[0] === "confirm")[1];
|
||||
payload.resolve(false);
|
||||
await expect(pending).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it("passes an optional title to the in-app dialog", async () => {
|
||||
const pending = DialogUtils.confirm("All messages will be lost.", "Delete conversations");
|
||||
const payload = GlobalEmitter.emit.mock.calls.find((c) => c[0] === "confirm")[1];
|
||||
expect(payload.title).toBe("Delete conversations");
|
||||
payload.resolve(true);
|
||||
await expect(pending).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it("aliases confirmCustom to the same in-app dialog", async () => {
|
||||
const pending = DialogUtils.confirmCustom("Leave room?");
|
||||
expect(GlobalEmitter.emit).toHaveBeenCalledWith(
|
||||
"confirm",
|
||||
expect.objectContaining({
|
||||
message: "Leave room?",
|
||||
resolve: expect.any(Function),
|
||||
})
|
||||
);
|
||||
const payload = GlobalEmitter.emit.mock.calls.find((c) => c[0] === "confirm")[1];
|
||||
payload.resolve(true);
|
||||
await expect(pending).resolves.toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("DialogUtils.prompt", () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(GlobalEmitter.emit).mockClear();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`UI snapshot regression > ConfirmDialog.vue > visible confirm dialog 1`] = `"<div="" class="fixed inset-0 z-9999 flex items-center justify-center p-4 confirm-dialog-enter-from confirm-dialog-enter-active"><div="" class="fixed inset-0 bg-black/50 backdrop-blur-xs shadow-2xl"></div><div="" class="relative w-full sm:w-auto sm:min-w-[400px] sm:max-w-md bg-white dark:bg-zinc-900 sm:rounded-3xl rounded-3xl shadow-2xl border border-gray-200 dark:border-zinc-800 overflow-hidden transform transition-all"><div="" class="p-8"><div="" class="flex items-start mb-6"><div="" class="shrink-0 flex items-center justify-center w-12 h-12 rounded-2xl bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-400 mr-4"><span="" class="mdi-snapshot w-6 h-6" data-icon="alert-circle"></span></div><div="" class="flex-1 min-w-0"><h3="" class="text-xl font-black text-gray-900 dark:text-white mb-2">common.confirm_action</h3><p="" class="text-gray-600 dark:text-zinc-300 whitespace-pre-wrap leading-relaxed">Delete this item?</p></div></div><div="" class="flex flex-col sm:flex-row gap-3 sm:justify-end mt-8"><button="" type="button" class="px-6 py-3 text-sm font-bold text-gray-700 dark:text-zinc-300 bg-gray-100 dark:bg-zinc-800 rounded-xl hover:bg-gray-200 dark:hover:bg-zinc-700 transition-all active:scale-95">common.cancel</button><button="" type="button" class="px-6 py-3 text-sm font-bold text-white bg-red-600 hover:bg-red-700 rounded-xl shadow-lg shadow-red-600/20 transition-all active:scale-95">common.confirm</button></div></div></div></div>"`;
|
||||
exports[`UI snapshot regression > ConfirmDialog.vue > visible confirm dialog 1`] = `"<div="" class="fixed inset-0 z-9999 flex items-center justify-center p-4 confirm-dialog-enter-from confirm-dialog-enter-active" role="alertdialog" aria-modal="true" aria-labelledby="confirm-dialog-title" aria-describedby="confirm-dialog-message"><div="" class="fixed inset-0 bg-black/50 backdrop-blur-xs shadow-2xl"></div><div="" class="relative w-full sm:w-auto sm:min-w-[400px] sm:max-w-md bg-white dark:bg-zinc-900 sm:rounded-3xl rounded-3xl shadow-2xl border border-gray-200 dark:border-zinc-800 overflow-hidden transform transition-all" tabindex="-1"><div="" class="p-8"><div="" class="flex items-start mb-6"><div="" class="shrink-0 flex items-center justify-center w-12 h-12 rounded-2xl bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-400 mr-4"><span="" class="mdi-snapshot w-6 h-6" data-icon="alert-circle"></span></div><div="" class="flex-1 min-w-0"><h3="" class="text-xl font-black text-gray-900 dark:text-white mb-2">common.confirm_action</h3><p="" class="text-gray-600 dark:text-zinc-300 whitespace-pre-wrap leading-relaxed">Delete this item?</p></div></div><div="" class="flex flex-col sm:flex-row gap-3 sm:justify-end mt-8"><button="" type="button" class="px-6 py-3 text-sm font-bold text-gray-700 dark:text-zinc-300 bg-gray-100 dark:bg-zinc-800 rounded-xl hover:bg-gray-200 dark:hover:bg-zinc-700 transition-all active:scale-95">common.cancel</button><button="" type="button" class="px-6 py-3 text-sm font-bold text-white bg-red-600 hover:bg-red-700 rounded-xl shadow-lg shadow-red-600/20 transition-all active:scale-95">common.confirm</button></div></div></div></div>"`;
|
||||
|
||||
exports[`UI snapshot regression > FormLabel.vue > default label 1`] = `"<label class="block text-sm font-medium text-gray-900 dark:text-zinc-100">Display name</label>"`;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue