Share, export, clusters and the pin hint stop being typed in English

Second of the shared-library batches, in the shepherd's order: ShareButton,
DataTable, LegendTools, and PinHint's dismiss label.

★ THE ONE THAT MATTERS MOST IS THE PARAGRAPH A PARTNER READS. ShareButton's
embed tab explains where to paste the snippet — "WordPress, Squarespace, Wix, or
any site that accepts HTML widgets" — and it was English on every locale of a
product whose whole pitch is that a partner can drop it into their own page. The
two tab labels beside it likewise.

Also wired: the data table's CSV and JSON buttons, its row-detail hint and
dialog label; the legend's Clusters button and the Minimum Spanning Tree option
with its explanation; and the pin hint's Dismiss.

★ SIX SITES NEEDED NO NEW WORDING AT ALL — they already had translations in all
eleven locales and were still typed into the template: four "Close" buttons
(buttons.close) across PosterDialog, DataTable, MapControlMenu and the QA
corner, "Expand legend" (aria.expandLegend) and PosterWall's "people groups".
Those are the cheapest defect in the whole catalogue: the work was done, the
reader just never saw it.

CSV and JSON are keyed but identical in all eleven, like DPI in the poster
batch. A format acronym is not an untranslated string; the key exists so a
language that does translate it can, without anyone editing a component.

★ LIBRARY CHANGE — studio's to land under rule 5B. I have NOT rebuilt anything.
Until it lands, every string falls back to the English it reads today.

Measured with the source audit: shared-library strings with no catalogue entry
go 32 → 21, and the has-a-key-but-hardcoded class goes 6 → 2. Remaining, in
order: QaCorner (10, a developer tool), SemanticTreeLegend (4), PosterPreview,
PosterWall and VerticalTabsLayout (2 each).
This commit is contained in:
Doxa Maps 2026-09-26 11:44:41 +00:00
parent 1795fb4740
commit 83fc3f5029
51 changed files with 237 additions and 36 deletions

View file

@ -38,6 +38,12 @@
* dropped from the custom element's shadow DOM (see WARNING.md).
*/
import { ref, inject, computed, onMounted, onBeforeUnmount, getCurrentInstance, nextTick } from 'vue'
import { tStatic } from '@map/i18n/index.js'
// This component's chrome, read out of the catalogue. tStatic and not useI18n: these mount inside
// custom elements that install no i18n plugin, and an unguarded useI18n() throws on the way in and
// takes the subtree with it. The English stays as the fallback.
const T = (key, en) => tStatic(key) || en
import { useI18n } from 'vue-i18n'
import { useShadowStyles } from '../../composables/useShadowStyles.js'
@ -374,7 +380,7 @@ useShadowStyles(`
<span class="pin-hint-pill">
<span class="pin-hint-dot" aria-hidden="true"></span>
{{ label }}
<button class="pin-hint-x" type="button" aria-label="Dismiss hint" @click="dismiss">×</button>
<button class="pin-hint-x" type="button" :aria-label="T('aria.dismissHint', 'Dismiss hint')" @click="dismiss">×</button>
</span>
</div>
</template>

View file

@ -1,5 +1,11 @@
<script setup>
import { ref, computed, onMounted, onBeforeUnmount, watch, inject, toRef } from 'vue'
import { tStatic } from '@map/i18n/index.js'
// This component's chrome, read out of the catalogue. tStatic and not useI18n: these mount inside
// custom elements that install no i18n plugin, and an unguarded useI18n() throws on the way in and
// takes the subtree with it. The English stays as the fallback.
const T = (key, en) => tStatic(key) || en
import { useI18n } from 'vue-i18n'
import { useShadowStyles } from '../../composables/useShadowStyles.js'
import { useLegendData } from '../../composables/legends/useLegendData.js'
@ -357,7 +363,7 @@ onBeforeUnmount(() => {
<!-- Collapsed pill — identical to STL's .stl-reopen -->
<button v-else-if="isCollapsed"
:class="['legend-reopen', { 'legend-reopen-light': !isDark }]"
title="Expand legend"
:title="T('aria.expandLegend', 'Expand legend')"
@click="isCollapsed = false">
<svg class="legend-reopen-icon" width="11" height="11" viewBox="0 0 12 12" fill="none">
<path d="M2 4h8M2 6h8M2 8h5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>

View file

@ -38,6 +38,12 @@ export function showsFlyTools(legendType) {
* parent profile listens through mapStore).
*/
import { ref, computed, inject, onBeforeUnmount, toRef } from 'vue'
import { tStatic } from '@map/i18n/index.js'
// This component's chrome, read out of the catalogue. tStatic and not useI18n: these mount inside
// custom elements that install no i18n plugin, and an unguarded useI18n() throws on the way in and
// takes the subtree with it. The English stays as the fallback.
const T = (key, en) => tStatic(key) || en
import { useI18n } from 'vue-i18n'
import { useShadowStyles } from '../../composables/useShadowStyles.js'
import { useLegendData } from '../../composables/legends/useLegendData.js'
@ -249,7 +255,7 @@ onBeforeUnmount(() => {
<path d="M2 6l3 3 5-6" stroke="#3498db" stroke-width="2" fill="none"/>
</svg>
</span>
<span class="lt-btn-text">Clusters</span>
<span class="lt-btn-text">{{ T('controls.clusters', 'Clusters') }}</span>
</button>
<button
class="lt-side-btn"
@ -261,7 +267,7 @@ onBeforeUnmount(() => {
<div v-if="showClusterSettings" class="lt-popup" @click.stop>
<div class="lt-popup-row active" @click="setClusterMode('mst')">
<span class="lt-popup-icon">⌬</span>
<span>Minimum Spanning Tree<br><small style="opacity:0.7;font-weight:400">click a legend row to cluster only that group</small></span>
<span>{{ T('controls.mst_full_name', 'Minimum Spanning Tree') }}<br><small style="opacity:0.7;font-weight:400">{{ T('controls.mst_hint', 'click a legend row to cluster only that group') }}</small></span>
</div>
</div>
<div v-if="showClusterSettings" ref="scrimRef" :style="scrimStyle" class="lt-overlay" @click.stop="showClusterSettings = false"></div>

View file

@ -27,6 +27,12 @@
-->
<script setup>
import { ref, inject, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'
import { tStatic } from '@map/i18n/index.js'
// This component's chrome, read out of the catalogue. tStatic and not useI18n: these mount inside
// custom elements that install no i18n plugin, and an unguarded useI18n() throws on the way in and
// takes the subtree with it. The English stays as the fallback.
const T = (key, en) => tStatic(key) || en
import { useShadowStyles } from '../../composables/useShadowStyles.js'
useShadowStyles(`
@ -140,7 +146,7 @@ onBeforeUnmount(() => {
<div v-if="open" ref="el" class="mcm" :class="{ light: !isDark }" :style="style" role="dialog" :aria-label="title">
<div class="mcm-header">
<span class="mcm-title">{{ title }}</span>
<button class="mcm-close" type="button" aria-label="Close" @click="emit('close')">
<button class="mcm-close" type="button" :aria-label="T('buttons.close', 'Close')" @click="emit('close')">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<line x1="2" y1="2" x2="12" y2="12"/><line x1="12" y1="2" x2="2" y2="12"/>
</svg>

View file

@ -22,6 +22,12 @@
-->
<script setup>
import { ref, computed, inject, unref, provide } from 'vue'
import { tStatic } from '@map/i18n/index.js'
// This component's chrome, read out of the catalogue. tStatic and not useI18n: these mount inside
// custom elements that install no i18n plugin, and an unguarded useI18n() throws on the way in and
// takes the subtree with it. The English stays as the fallback.
const T = (key, en) => tStatic(key) || en
import { useI18n } from 'vue-i18n'
import { useShadowStyles } from '../../composables/useShadowStyles.js'
import { resolveShareUrl } from '../../utils/embedBase.js'
@ -538,12 +544,12 @@ function bindAnchor(el) {
class="share-tab"
:class="{ active: activeTab === 'link' }"
@click="activeTab = 'link'"
>Embed link</button>
>{{ T('share.embed_link', 'Embed link') }}</button>
<button
class="share-tab"
:class="{ active: activeTab === 'embed' }"
@click="activeTab = 'embed'"
>Add to Your Website</button>
>{{ T('share.add_to_website', 'Add to Your Website') }}</button>
</div>
<!-- ===== Tab 1: Share Link ===== -->
@ -579,7 +585,7 @@ function bindAnchor(el) {
<div v-if="activeTab === 'embed'" class="share-body">
<!-- Friendly description -->
<div class="share-embed-desc">
Paste this code into your website builder (WordPress, Squarespace, Wix, or any site that accepts HTML widgets).
{{ T('share.embed_paste_hint', 'Paste this code into your website builder (WordPress, Squarespace, Wix, or any site that accepts HTML widgets).') }}
</div>
<!-- Size presets -->

View file

@ -88,7 +88,7 @@
</div>
<div class="actions">
<button class="btn btn--ghost" type="button" @click="onClose">Close</button>
<button class="btn btn--ghost" type="button" @click="onClose">{{ T('buttons.close', 'Close') }}</button>
<button class="btn btn--primary" type="button" :disabled="poster.isRendering.value" @click="onRender">
{{ poster.isRendering.value ? 'Rendering…' : `Render ${format.toUpperCase()}` }}
</button>

View file

@ -33,6 +33,12 @@
* ADD to declared widths and columns drift.
*/
import { ref, shallowRef, onMounted, onBeforeUnmount, watch, computed } from 'vue'
import { tStatic } from '@map/i18n/index.js'
// This component's chrome, read out of the catalogue. tStatic and not useI18n: these mount inside
// custom elements that install no i18n plugin, and an unguarded useI18n() throws on the way in and
// takes the subtree with it. The English stays as the fallback.
const T = (key, en) => tStatic(key) || en
import { useShadowStyles } from '../../composables/useShadowStyles.js'
// A fixed;inset:0 scrim covers only the CONTENT PANE inside the cap-5 shell (contain:layout on
// .vtl-content captures the fixed containing block). Measured here: 1024x793 @ (256,107) at a
@ -297,22 +303,22 @@ useShadowStyles(
<span class="dt-count">{{ rowCount.toLocaleString() }} rows</span>
<span v-if="meta.grain" class="dt-grain">· {{ meta.grain }}</span>
<span class="dt-actions">
<button type="button" class="dt-btn" @click="download('csv')">CSV</button>
<button type="button" class="dt-btn" @click="download('json')">JSON</button>
<button type="button" class="dt-btn" @click="download('csv')">{{ T('tables.csv', 'CSV') }}</button>
<button type="button" class="dt-btn" @click="download('json')">{{ T('tables.json', 'JSON') }}</button>
</span>
</div>
<div v-if="err" class="dt-err">{{ err }}</div>
<div class="dt-wrap"><div ref="host"></div></div>
<p class="dt-hint">Tap any row for its full detail{{ isMobile ? '' : ' — every field, including the lists the table flattens' }}.</p>
<p class="dt-hint">{{ T('tables.tap_row_hint', 'Tap any row for its full detail') }}{{ isMobile ? '' : ' — every field, including the lists the table flattens' }}.</p>
<!-- ROW DETAIL — a bottom sheet on mobile, a panel on desktop. Carries EVERY
field, including the ones the list view has to hide or flatten. -->
<div v-if="detail" ref="scrimRef" :style="scrimStyle" class="dt-scrim" @click.self="detail = null">
<div class="dt-sheet" role="dialog" aria-label="Row detail">
<div class="dt-sheet" role="dialog" :aria-label="T('aria.rowDetail', 'Row detail')">
<div class="dt-sheet-head">
<strong>{{ detail[(table?.columns || [])[0]?.field] || 'Detail' }}</strong>
<button type="button" class="dt-close" @click="detail = null">Close</button>
<button type="button" class="dt-close" @click="detail = null">{{ T('buttons.close', 'Close') }}</button>
</div>
<dl class="dt-dl">
<template v-for="[k, v] in detailRows" :key="k">

View file

@ -7,5 +7,7 @@
"searchPeopleGroups": "ابحث عن الشعوب",
"resetAllFilters": "إعادة تعيين كل عوامل التصفية والبحث",
"selectAllRows": "تحديد كل الصفوف",
"deselectAllRows": "إلغاء تحديد كل الصفوف"
"deselectAllRows": "إلغاء تحديد كل الصفوف",
"rowDetail": "تفاصيل الصف",
"dismissHint": "إخفاء التلميح"
}

View file

@ -34,5 +34,8 @@
"prayer_status": "حالة الصلاة",
"close": "إغلاق",
"clear": "مسح",
"map_controls": "أدوات التحكم بالخريطة"
"map_controls": "أدوات التحكم بالخريطة",
"clusters": "التجمّعات",
"mst_full_name": "الشجرة الممتدة الصغرى",
"mst_hint": "اضغط على صف في مفتاح الخريطة لتجميع تلك المجموعة وحدها"
}

View file

@ -0,0 +1,5 @@
{
"embed_link": "رابط التضمين",
"add_to_website": "أضِفها إلى موقعك",
"embed_paste_hint": "الصق هذا الرمز في أداة إنشاء موقعك (WordPress أو Squarespace أو Wix أو أي موقع يقبل أدوات HTML)."
}

View file

@ -0,0 +1,5 @@
{
"csv": "CSV",
"json": "JSON",
"tap_row_hint": "اضغط على أي صف لعرض تفاصيله الكاملة"
}

View file

@ -7,5 +7,7 @@
"searchPeopleGroups": "Volksgruppen suchen",
"resetAllFilters": "Alle Filter und die Suche zurücksetzen",
"selectAllRows": "Alle Zeilen auswählen",
"deselectAllRows": "Auswahl aller Zeilen aufheben"
"deselectAllRows": "Auswahl aller Zeilen aufheben",
"rowDetail": "Zeilendetails",
"dismissHint": "Hinweis schließen"
}

View file

@ -34,5 +34,8 @@
"prayer_status": "Gebetsstatus",
"close": "Schließen",
"clear": "Zurücksetzen",
"map_controls": "Kartensteuerung"
"map_controls": "Kartensteuerung",
"clusters": "Cluster",
"mst_full_name": "Minimaler Spannbaum",
"mst_hint": "Klicken Sie auf eine Legendenzeile, um nur diese Gruppe zu clustern"
}

View file

@ -0,0 +1,5 @@
{
"embed_link": "Einbettungslink",
"add_to_website": "Zu Ihrer Website hinzufügen",
"embed_paste_hint": "Fügen Sie diesen Code in Ihren Website-Baukasten ein (WordPress, Squarespace, Wix oder jede Website, die HTML-Widgets akzeptiert)."
}

View file

@ -0,0 +1,5 @@
{
"csv": "CSV",
"json": "JSON",
"tap_row_hint": "Tippen Sie auf eine Zeile für alle Details"
}

View file

@ -7,5 +7,7 @@
"searchPeopleGroups": "Search people groups",
"resetAllFilters": "Reset all filters and search",
"selectAllRows": "Select all rows",
"deselectAllRows": "Deselect all rows"
"deselectAllRows": "Deselect all rows",
"rowDetail": "Row detail",
"dismissHint": "Dismiss hint"
}

View file

@ -34,5 +34,8 @@
"prayer_status": "Prayer Status",
"close": "Close",
"clear": "Clear",
"map_controls": "Research map controls"
"map_controls": "Research map controls",
"clusters": "Clusters",
"mst_full_name": "Minimum Spanning Tree",
"mst_hint": "click a legend row to cluster only that group"
}

View file

@ -0,0 +1,5 @@
{
"embed_link": "Embed link",
"add_to_website": "Add to Your Website",
"embed_paste_hint": "Paste this code into your website builder (WordPress, Squarespace, Wix, or any site that accepts HTML widgets)."
}

View file

@ -0,0 +1,5 @@
{
"csv": "CSV",
"json": "JSON",
"tap_row_hint": "Tap any row for its full detail"
}

View file

@ -7,5 +7,7 @@
"searchPeopleGroups": "Buscar pueblos",
"resetAllFilters": "Restablecer todos los filtros y la búsqueda",
"selectAllRows": "Seleccionar todas las filas",
"deselectAllRows": "Deseleccionar todas las filas"
"deselectAllRows": "Deseleccionar todas las filas",
"rowDetail": "Detalle de la fila",
"dismissHint": "Descartar la sugerencia"
}

View file

@ -34,5 +34,8 @@
"prayer_status": "Estado de oración",
"close": "Cerrar",
"clear": "Borrar",
"map_controls": "Controles del mapa"
"map_controls": "Controles del mapa",
"clusters": "Clústeres",
"mst_full_name": "Árbol de expansión mínima",
"mst_hint": "haz clic en una fila de la leyenda para agrupar solo ese grupo"
}

View file

@ -0,0 +1,5 @@
{
"embed_link": "Enlace para insertar",
"add_to_website": "Añádelo a tu sitio web",
"embed_paste_hint": "Pega este código en tu creador de sitios web (WordPress, Squarespace, Wix o cualquier sitio que acepte widgets HTML)."
}

View file

@ -0,0 +1,5 @@
{
"csv": "CSV",
"json": "JSON",
"tap_row_hint": "Toca cualquier fila para ver su detalle completo"
}

View file

@ -7,5 +7,7 @@
"searchPeopleGroups": "Rechercher des peuples",
"resetAllFilters": "Réinitialiser tous les filtres et la recherche",
"selectAllRows": "Sélectionner toutes les lignes",
"deselectAllRows": "Désélectionner toutes les lignes"
"deselectAllRows": "Désélectionner toutes les lignes",
"rowDetail": "Détail de la ligne",
"dismissHint": "Masquer l'astuce"
}

View file

@ -34,5 +34,8 @@
"prayer_status": "État de prière",
"close": "Fermer",
"clear": "Effacer",
"map_controls": "Commandes de la carte"
"map_controls": "Commandes de la carte",
"clusters": "Groupes",
"mst_full_name": "Arbre couvrant minimal",
"mst_hint": "cliquez sur une ligne de la légende pour ne regrouper que ce groupe"
}

View file

@ -0,0 +1,5 @@
{
"embed_link": "Lien d'intégration",
"add_to_website": "Ajouter à votre site web",
"embed_paste_hint": "Collez ce code dans votre créateur de site web (WordPress, Squarespace, Wix ou tout site acceptant les widgets HTML)."
}

View file

@ -0,0 +1,5 @@
{
"csv": "CSV",
"json": "JSON",
"tap_row_hint": "Touchez une ligne pour voir tout son détail"
}

View file

@ -7,5 +7,7 @@
"searchPeopleGroups": "जनसमूह खोजें",
"resetAllFilters": "सभी फ़िल्टर और खोज रीसेट करें",
"selectAllRows": "सभी पंक्तियाँ चुनें",
"deselectAllRows": "सभी पंक्तियाँ अचयनित करें"
"deselectAllRows": "सभी पंक्तियाँ अचयनित करें",
"rowDetail": "पंक्ति विवरण",
"dismissHint": "संकेत हटाएँ"
}

View file

@ -34,5 +34,8 @@
"prayer_status": "प्रार्थना स्थिति",
"close": "बंद करें",
"clear": "साफ़ करें",
"map_controls": "मानचित्र नियंत्रण"
"map_controls": "मानचित्र नियंत्रण",
"clusters": "क्लस्टर",
"mst_full_name": "न्यूनतम विस्तारित वृक्ष",
"mst_hint": "केवल उस समूह को क्लस्टर करने के लिए लेजेंड की पंक्ति पर क्लिक करें"
}

View file

@ -0,0 +1,5 @@
{
"embed_link": "एम्बेड लिंक",
"add_to_website": "अपनी वेबसाइट में जोड़ें",
"embed_paste_hint": "इस कोड को अपने वेबसाइट बिल्डर में पेस्ट करें (WordPress, Squarespace, Wix, या कोई भी साइट जो HTML विजेट स्वीकार करती है)।"
}

View file

@ -0,0 +1,5 @@
{
"csv": "CSV",
"json": "JSON",
"tap_row_hint": "पूरा विवरण देखने के लिए किसी भी पंक्ति पर टैप करें"
}

View file

@ -7,5 +7,7 @@
"searchPeopleGroups": "Cerca popoli",
"resetAllFilters": "Reimposta tutti i filtri e la ricerca",
"selectAllRows": "Seleziona tutte le righe",
"deselectAllRows": "Deseleziona tutte le righe"
"deselectAllRows": "Deseleziona tutte le righe",
"rowDetail": "Dettaglio riga",
"dismissHint": "Ignora il suggerimento"
}

View file

@ -34,5 +34,8 @@
"prayer_status": "Stato di preghiera",
"close": "Chiudi",
"clear": "Cancella",
"map_controls": "Controlli della mappa"
"map_controls": "Controlli della mappa",
"clusters": "Cluster",
"mst_full_name": "Albero ricoprente minimo",
"mst_hint": "fai clic su una riga della legenda per raggruppare solo quel gruppo"
}

View file

@ -0,0 +1,5 @@
{
"embed_link": "Link di incorporamento",
"add_to_website": "Aggiungi al tuo sito web",
"embed_paste_hint": "Incolla questo codice nel tuo costruttore di siti web (WordPress, Squarespace, Wix o qualsiasi sito che accetti widget HTML)."
}

View file

@ -0,0 +1,5 @@
{
"csv": "CSV",
"json": "JSON",
"tap_row_hint": "Tocca una riga per vederne il dettaglio completo"
}

View file

@ -7,5 +7,7 @@
"searchPeopleGroups": "Pesquisar povos",
"resetAllFilters": "Redefinir todos os filtros e a pesquisa",
"selectAllRows": "Selecionar todas as linhas",
"deselectAllRows": "Desmarcar todas as linhas"
"deselectAllRows": "Desmarcar todas as linhas",
"rowDetail": "Detalhe da linha",
"dismissHint": "Dispensar a dica"
}

View file

@ -34,5 +34,8 @@
"prayer_status": "Estado de oração",
"close": "Fechar",
"clear": "Limpar",
"map_controls": "Controlos do mapa"
"map_controls": "Controlos do mapa",
"clusters": "Agrupamentos",
"mst_full_name": "Árvore geradora mínima",
"mst_hint": "clique numa linha da legenda para agrupar apenas esse grupo"
}

View file

@ -0,0 +1,5 @@
{
"embed_link": "Link de incorporação",
"add_to_website": "Adicione ao seu site",
"embed_paste_hint": "Cole este código no seu construtor de sites (WordPress, Squarespace, Wix ou qualquer site que aceite widgets HTML)."
}

View file

@ -0,0 +1,5 @@
{
"csv": "CSV",
"json": "JSON",
"tap_row_hint": "Toque em qualquer linha para ver o detalhe completo"
}

View file

@ -7,5 +7,7 @@
"searchPeopleGroups": "Caută popoare",
"resetAllFilters": "Resetează toate filtrele și căutarea",
"selectAllRows": "Selectează toate rândurile",
"deselectAllRows": "Deselectează toate rândurile"
"deselectAllRows": "Deselectează toate rândurile",
"rowDetail": "Detaliu rând",
"dismissHint": "Închide sugestia"
}

View file

@ -34,5 +34,8 @@
"prayer_status": "Stare de rugăciune",
"close": "Închide",
"clear": "Șterge",
"map_controls": "Comenzile hărții"
"map_controls": "Comenzile hărții",
"clusters": "Grupuri",
"mst_full_name": "Arbore de acoperire minim",
"mst_hint": "dă clic pe un rând din legendă pentru a grupa doar acel grup"
}

View file

@ -0,0 +1,5 @@
{
"embed_link": "Link de încorporare",
"add_to_website": "Adaugă pe site-ul tău",
"embed_paste_hint": "Lipește acest cod în constructorul site-ului tău (WordPress, Squarespace, Wix sau orice site care acceptă widget-uri HTML)."
}

View file

@ -0,0 +1,5 @@
{
"csv": "CSV",
"json": "JSON",
"tap_row_hint": "Atinge orice rând pentru detaliile complete"
}

View file

@ -7,5 +7,7 @@
"searchPeopleGroups": "Поиск народов",
"resetAllFilters": "Сбросить все фильтры и поиск",
"selectAllRows": "Выбрать все строки",
"deselectAllRows": "Снять выделение со всех строк"
"deselectAllRows": "Снять выделение со всех строк",
"rowDetail": "Подробности строки",
"dismissHint": "Скрыть подсказку"
}

View file

@ -34,5 +34,8 @@
"prayer_status": "Статус молитвы",
"close": "Закрыть",
"clear": "Очистить",
"map_controls": "Элементы управления картой"
"map_controls": "Элементы управления картой",
"clusters": "Кластеры",
"mst_full_name": "Минимальное остовное дерево",
"mst_hint": "нажмите строку легенды, чтобы сгруппировать только эту группу"
}

View file

@ -0,0 +1,5 @@
{
"embed_link": "Ссылка для встраивания",
"add_to_website": "Добавить на ваш сайт",
"embed_paste_hint": "Вставьте этот код в конструктор вашего сайта (WordPress, Squarespace, Wix или любой сайт, принимающий HTML-виджеты)."
}

View file

@ -0,0 +1,5 @@
{
"csv": "CSV",
"json": "JSON",
"tap_row_hint": "Нажмите на любую строку, чтобы увидеть все подробности"
}

View file

@ -7,5 +7,7 @@
"searchPeopleGroups": "搜索民族",
"resetAllFilters": "重置所有筛选和搜索",
"selectAllRows": "选择所有行",
"deselectAllRows": "取消选择所有行"
"deselectAllRows": "取消选择所有行",
"rowDetail": "行详情",
"dismissHint": "关闭提示"
}

View file

@ -34,5 +34,8 @@
"prayer_status": "代祷状态",
"close": "关闭",
"clear": "清除",
"map_controls": "地图控件"
"map_controls": "地图控件",
"clusters": "聚类",
"mst_full_name": "最小生成树",
"mst_hint": "点击图例中的一行,仅对该组进行聚类"
}

View file

@ -0,0 +1,5 @@
{
"embed_link": "嵌入链接",
"add_to_website": "添加到您的网站",
"embed_paste_hint": "将此代码粘贴到您的网站构建器中(WordPress、Squarespace、Wix,或任何接受 HTML 小部件的网站)。"
}

View file

@ -0,0 +1,5 @@
{
"csv": "CSV",
"json": "JSON",
"tap_row_hint": "点按任一行查看完整详情"
}