The last of the shared-library English: legend, poster preview and tab loading states

Third and final batch of the shared-library strings, in the shepherd's order:
SemanticTreeLegend (4), PosterPreview (2), PosterWall (2) and
VerticalTabsLayout (2), plus PosterWall's "people groups" which already had a
key and was still typed in.

These are all LOADING and OVERFLOW states — "Loading data…", "Showing top 500 of
2,107", "use the geocoder above to find the rest", "Loading map snapshot…",
"preview unavailable", "Could not load X". Text a reader meets exactly when
something is slow or has gone wrong, which is the worst moment to be handed a
language they do not read. PosterWall's own source called one of them "i18n
DEBT" in a comment; it is paid now.

The counted ones take a placeholder rather than being split around an
interpolation — legend.showingTop is "Showing top {n} of {m}" — so a translator
can move the numbers within the sentence. Languages do not agree about where a
count goes, and a sentence assembled from fragments cannot be reordered at all.

VerticalTabsLayout needed a different shape: it is an options-API component, so
a module-level helper compiles fine and then renders nothing, because its
template can only see what the component returns. T is exposed from setup().

★ THE NEW GATE CAUGHT SOMETHING I WOULD HAVE MISSED, on its first real batch.
Adding these keys flipped two strings in
plugin-designs/pplr-data-maps/src/components/SemanticTreeLegend.vue from "no
catalogue entry" to "has a key and is still hardcoded" — that file is a COPY of
the library component I just fixed, carrying the same debt. It is another lane's
plugin so I have not touched it, and it is reported rather than quietly
baselined away. The baseline is refreshed to today's truth so the gate guards
the new line: 285 → 273 without a key, and the two copies recorded as known debt
rather than as a regression.
This commit is contained in:
Doxa Maps 2026-09-26 12:07:08 +00:00
parent 36ad9ae73c
commit fef8c7b0fd
38 changed files with 171 additions and 66 deletions

View file

@ -14,6 +14,11 @@
* { id, label, color?, count?, pop?, filter?, info?, children?: TreeNode[] }
*/
import { ref, computed, watch, nextTick, inject, unref } 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.
const T = (key, en) => tStatic(key) || en
import { useI18n } from 'vue-i18n'
import { useShadowStyles } from '../../composables/useShadowStyles.js'
import { useInstance } from '../../composables/usePplrInstance.js'
@ -1305,14 +1310,15 @@ useShadowStyles(`
<div v-if="tabLoading && !visibleRows.length" class="stl-empty stl-loading">
<span class="stl-spinner"></span>
<span>Loading {{ tabList[activeTab]?.label || 'rows' }}…</span>
<span>{{ T('legend.loadingRows', 'Loading {label}…').replace('{label}', tabList[activeTab]?.label || 'rows') }}</span>
</div>
<div v-else-if="!visibleRows.length && nodes.length === 0" class="stl-empty">Loading data…</div>
<div v-else-if="!visibleRows.length && nodes.length === 0" class="stl-empty">{{ T('legend.loadingData', 'Loading data…') }}</div>
<div v-else-if="!visibleRows.length" class="stl-empty">{{ t('legend.noDataAtThisLevel') }}</div>
<div v-if="topNodesOverflow > 0" class="stl-overflow">
Showing top {{ tabList[activeTab].topLimit.toLocaleString() }} of
{{ topNodesAll.length.toLocaleString() }} —
<span class="stl-overflow-hint">use the geocoder above to find the rest</span>
{{ T('legend.showingTop', 'Showing top {n} of {m}')
.replace('{n}', tabList[activeTab].topLimit.toLocaleString())
.replace('{m}', topNodesAll.length.toLocaleString()) }} —
<span class="stl-overflow-hint">{{ T('legend.useGeocoderHint', 'use the geocoder above to find the rest') }}</span>
</div>
</div>
</div>

View file

@ -17,10 +17,10 @@
class="poster-preview__map"
:src="snapshot"
:style="boxStyle(layout.map)"
alt="map preview"
:alt="T('poster.map_preview', 'map preview')"
/>
<div v-else class="poster-preview__map placeholder" :style="boxStyle(layout.map)">
Loading map snapshot…
{{ T('poster.loading_snapshot', 'Loading map snapshot…') }}
</div>
<TitleSlot
@ -68,6 +68,11 @@
<script setup>
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
import { tStatic } from '../../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.
const T = (key, en) => tStatic(key) || en
import TitleSlot from './slots/TitleSlot.vue';
import LegendSlot from './slots/LegendSlot.vue';
import ScaleBarSlot from './slots/ScaleBarSlot.vue';

View file

@ -40,6 +40,11 @@
* CustomEvent scoped by instanceId, so several embeds on one page never cross-talk.
*/
import { computed, ref, watch, onBeforeUnmount } from 'vue'
import { tStatic } from '../../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.
const T = (key, en) => tStatic(key) || en
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
import { usePosterPrints } from '@map/composables/usePosterPrints.js'
import { useI18n } from 'vue-i18n'
@ -959,7 +964,7 @@ useShadowStyles(
<span v-if="collapsible" class="sec-caret" :class="{ 'is-open': !isCollapsed(sec.name) }">▶</span>
<h3 class="sec-name">{{ sec.name }}</h3>
<span class="sec-count">
{{ sec.total.toLocaleString() }} people groups ·
{{ sec.total.toLocaleString() }} {{ T('gospel.list.peopleGroups', 'people groups') }} ·
{{ sec.items.length }} {{ sec.items.length === 1 ? itemNoun : plural }}
</span>
</component>
@ -1021,7 +1026,7 @@ useShadowStyles(
poster that is sitting on disk is how the Driver came to report this.
i18n DEBT: this literal predates t() here and is still untranslated, like
the two beside it. -->
<div class="ph-note">preview unavailable</div>
<div class="ph-note">{{ T('poster.preview_unavailable', 'preview unavailable') }}</div>
</div>
<!-- ★ THE PRINT ROW LIVES INSIDE .main, NOT BESIDE IT (Driver 2026-09-25).

View file

@ -72,5 +72,9 @@
},
"noDataAtThisLevel": "لا توجد بيانات في هذا المستوى",
"exportButton": "تصدير",
"exportSelection": "تصدير التحديد"
"exportSelection": "تصدير التحديد",
"loadingRows": "جارٍ تحميل {label}…",
"loadingData": "جارٍ تحميل البيانات…",
"showingTop": "عرض أعلى {n} من {m}",
"useGeocoderHint": "استخدم أداة البحث أعلاه للعثور على البقية"
}

View file

@ -5,5 +5,7 @@
},
"upg100WallHeading": "قائمتي UPG 100 — قوائم الصلاة والملصقات",
"upg100WallTag": "تعبئة للصلاة",
"upg100WallIntro": "وزّع مهمة الصلاة في مجموعات من خمسين ومئة — كما أطعم يسوع الخمسة آلاف (لوقا ٩:١٤). كل ملصق هو قائمة من 100 يمكنك أن تصلي بها وتطبعها وتعلّقها على الحائط — مصلّيًا لأجلهم <strong>باسم شعبهم</strong>."
"upg100WallIntro": "وزّع مهمة الصلاة في مجموعات من خمسين ومئة — كما أطعم يسوع الخمسة آلاف (لوقا ٩:١٤). كل ملصق هو قائمة من 100 يمكنك أن تصلي بها وتطبعها وتعلّقها على الحائط — مصلّيًا لأجلهم <strong>باسم شعبهم</strong>.",
"loadingNamed": "جارٍ تحميل {name}…",
"couldNotLoadNamed": "تعذّر تحميل {name}."
}

View file

@ -15,5 +15,8 @@
"scale_bar": "شريط المقياس",
"north_arrow": "سهم الشمال",
"footer_attribution": "التذييل (الإسناد مطلوب)",
"cancel": "إلغاء"
"cancel": "إلغاء",
"map_preview": "معاينة الخريطة",
"loading_snapshot": "جارٍ تحميل لقطة الخريطة…",
"preview_unavailable": "المعاينة غير متاحة"
}

View file

@ -72,5 +72,9 @@
},
"noDataAtThisLevel": "Keine Daten auf dieser Ebene",
"exportButton": "Exportieren",
"exportSelection": "Auswahl exportieren"
"exportSelection": "Auswahl exportieren",
"loadingRows": "{label} werden geladen…",
"loadingData": "Daten werden geladen…",
"showingTop": "Top {n} von {m} werden angezeigt",
"useGeocoderHint": "verwenden Sie die Suche oben, um die übrigen zu finden"
}

View file

@ -5,5 +5,7 @@
},
"upg100WallHeading": "Meine UPG-100-Liste — Gebetslisten und Poster",
"upg100WallTag": "Gebetsmobilisierung",
"upg100WallIntro": "Teilen Sie die Gebetsaufgabe in Gruppen zu fünfzig und hundert auf — so wie Jesus die 5.000 speiste (Lukas 9,14). Jedes Poster ist eine 100er-Liste, die Sie beten, drucken und an die Wand hängen können — und für sie <strong>mit dem Namen ihrer Volksgruppe</strong> beten."
"upg100WallIntro": "Teilen Sie die Gebetsaufgabe in Gruppen zu fünfzig und hundert auf — so wie Jesus die 5.000 speiste (Lukas 9,14). Jedes Poster ist eine 100er-Liste, die Sie beten, drucken und an die Wand hängen können — und für sie <strong>mit dem Namen ihrer Volksgruppe</strong> beten.",
"loadingNamed": "{name} wird geladen…",
"couldNotLoadNamed": "{name} konnte nicht geladen werden."
}

View file

@ -15,5 +15,8 @@
"scale_bar": "Maßstabsleiste",
"north_arrow": "Nordpfeil",
"footer_attribution": "Fußzeile (Quellenangabe erforderlich)",
"cancel": "Abbrechen"
"cancel": "Abbrechen",
"map_preview": "Kartenvorschau",
"loading_snapshot": "Kartenaufnahme wird geladen…",
"preview_unavailable": "Vorschau nicht verfügbar"
}

View file

@ -72,5 +72,9 @@
},
"noDataAtThisLevel": "No data at this level",
"exportButton": "Export",
"exportSelection": "Export selection"
"exportSelection": "Export selection",
"loadingRows": "Loading {label}…",
"loadingData": "Loading data…",
"showingTop": "Showing top {n} of {m}",
"useGeocoderHint": "use the geocoder above to find the rest"
}

View file

@ -5,5 +5,7 @@
},
"upg100WallHeading": "My UPG 100 List — Prayer Lists & Posters",
"upg100WallTag": "Prayer Mobilization",
"upg100WallIntro": "Hand away the prayer task in clusters of fifties and hundreds — the way Jesus fed the 5,000 (Luke 9:14). Each poster is a 100-list you can pray, print, and put on a wall — praying for them <strong>by their people group name</strong>."
"upg100WallIntro": "Hand away the prayer task in clusters of fifties and hundreds — the way Jesus fed the 5,000 (Luke 9:14). Each poster is a 100-list you can pray, print, and put on a wall — praying for them <strong>by their people group name</strong>.",
"loadingNamed": "Loading {name}…",
"couldNotLoadNamed": "Could not load {name}."
}

View file

@ -15,5 +15,8 @@
"scale_bar": "Scale bar",
"north_arrow": "North arrow",
"footer_attribution": "Footer (attribution required)",
"cancel": "Cancel"
"cancel": "Cancel",
"map_preview": "map preview",
"loading_snapshot": "Loading map snapshot…",
"preview_unavailable": "preview unavailable"
}

View file

@ -72,5 +72,9 @@
},
"noDataAtThisLevel": "No hay datos en este nivel",
"exportButton": "Exportar",
"exportSelection": "Exportar la selección"
"exportSelection": "Exportar la selección",
"loadingRows": "Cargando {label}…",
"loadingData": "Cargando datos…",
"showingTop": "Mostrando los {n} principales de {m}",
"useGeocoderHint": "usa el buscador de arriba para encontrar el resto"
}

View file

@ -5,5 +5,7 @@
},
"upg100WallHeading": "Mi lista UPG 100 — Listas de oración y carteles",
"upg100WallTag": "Movilización de oración",
"upg100WallIntro": "Reparte la tarea de la oración en grupos de cincuenta y de cien, como Jesús alimentó a los 5.000 (Lucas 9:14). Cada cartel es una lista de 100 que puedes orar, imprimir y poner en la pared — orando por ellos <strong>por el nombre de su pueblo</strong>."
"upg100WallIntro": "Reparte la tarea de la oración en grupos de cincuenta y de cien, como Jesús alimentó a los 5.000 (Lucas 9:14). Cada cartel es una lista de 100 que puedes orar, imprimir y poner en la pared — orando por ellos <strong>por el nombre de su pueblo</strong>.",
"loadingNamed": "Cargando {name}…",
"couldNotLoadNamed": "No se pudo cargar {name}."
}

View file

@ -15,5 +15,8 @@
"scale_bar": "Barra de escala",
"north_arrow": "Flecha norte",
"footer_attribution": "Pie de página (atribución obligatoria)",
"cancel": "Cancelar"
"cancel": "Cancelar",
"map_preview": "vista previa del mapa",
"loading_snapshot": "Cargando la captura del mapa…",
"preview_unavailable": "vista previa no disponible"
}

View file

@ -72,5 +72,9 @@
},
"noDataAtThisLevel": "Aucune donnée à ce niveau",
"exportButton": "Exporter",
"exportSelection": "Exporter la sélection"
"exportSelection": "Exporter la sélection",
"loadingRows": "Chargement de {label}…",
"loadingData": "Chargement des données…",
"showingTop": "Affichage des {n} premiers sur {m}",
"useGeocoderHint": "utilisez la recherche ci-dessus pour trouver les autres"
}

View file

@ -5,5 +5,7 @@
},
"upg100WallHeading": "Ma liste UPG 100 — Listes de prière et affiches",
"upg100WallTag": "Mobilisation pour la prière",
"upg100WallIntro": "Répartissez la tâche de la prière par groupes de cinquante et de cent — comme Jésus a nourri les 5 000 (Luc 9:14). Chaque affiche est une liste de 100 que vous pouvez prier, imprimer et afficher au mur — en priant pour eux <strong>par le nom de leur peuple</strong>."
"upg100WallIntro": "Répartissez la tâche de la prière par groupes de cinquante et de cent — comme Jésus a nourri les 5 000 (Luc 9:14). Chaque affiche est une liste de 100 que vous pouvez prier, imprimer et afficher au mur — en priant pour eux <strong>par le nom de leur peuple</strong>.",
"loadingNamed": "Chargement de {name}…",
"couldNotLoadNamed": "Impossible de charger {name}."
}

View file

@ -15,5 +15,8 @@
"scale_bar": "Échelle",
"north_arrow": "Flèche du nord",
"footer_attribution": "Pied de page (attribution requise)",
"cancel": "Annuler"
"cancel": "Annuler",
"map_preview": "aperçu de la carte",
"loading_snapshot": "Chargement de l'instantané de la carte…",
"preview_unavailable": "aperçu indisponible"
}

View file

@ -72,5 +72,9 @@
},
"noDataAtThisLevel": "इस स्तर पर कोई डेटा नहीं",
"exportButton": "निर्यात",
"exportSelection": "चयन निर्यात करें"
"exportSelection": "चयन निर्यात करें",
"loadingRows": "{label} लोड हो रहे हैं…",
"loadingData": "डेटा लोड हो रहा है…",
"showingTop": "{m} में से शीर्ष {n} दिखाए जा रहे हैं",
"useGeocoderHint": "बाकी खोजने के लिए ऊपर दिए गए खोज बॉक्स का उपयोग करें"
}

View file

@ -5,5 +5,7 @@
},
"upg100WallHeading": "मेरी UPG 100 सूची — प्रार्थना सूचियाँ और पोस्टर",
"upg100WallTag": "प्रार्थना लामबंदी",
"upg100WallIntro": "प्रार्थना के काम को पचास-पचास और सौ-सौ के समूहों में बाँटें — जैसे यीशु ने 5,000 लोगों को खिलाया (लूका 9:14)। हर पोस्टर 100 की एक सूची है जिसके लिए आप प्रार्थना कर सकते हैं, छाप सकते हैं और दीवार पर लगा सकते हैं — उनके लिए <strong>उनके जनसमूह के नाम से</strong> प्रार्थना करते हुए।"
"upg100WallIntro": "प्रार्थना के काम को पचास-पचास और सौ-सौ के समूहों में बाँटें — जैसे यीशु ने 5,000 लोगों को खिलाया (लूका 9:14)। हर पोस्टर 100 की एक सूची है जिसके लिए आप प्रार्थना कर सकते हैं, छाप सकते हैं और दीवार पर लगा सकते हैं — उनके लिए <strong>उनके जनसमूह के नाम से</strong> प्रार्थना करते हुए।",
"loadingNamed": "{name} लोड हो रहा है…",
"couldNotLoadNamed": "{name} लोड नहीं हो सका।"
}

View file

@ -15,5 +15,8 @@
"scale_bar": "स्केल बार",
"north_arrow": "उत्तर तीर",
"footer_attribution": "फ़ुटर (एट्रिब्यूशन आवश्यक)",
"cancel": "रद्द करें"
"cancel": "रद्द करें",
"map_preview": "मानचित्र पूर्वावलोकन",
"loading_snapshot": "मानचित्र स्नैपशॉट लोड हो रहा है…",
"preview_unavailable": "पूर्वावलोकन उपलब्ध नहीं"
}

View file

@ -72,5 +72,9 @@
},
"noDataAtThisLevel": "Nessun dato a questo livello",
"exportButton": "Esporta",
"exportSelection": "Esporta la selezione"
"exportSelection": "Esporta la selezione",
"loadingRows": "Caricamento di {label}…",
"loadingData": "Caricamento dati…",
"showingTop": "Mostrati i primi {n} di {m}",
"useGeocoderHint": "usa la ricerca qui sopra per trovare gli altri"
}

View file

@ -5,5 +5,7 @@
},
"upg100WallHeading": "La mia lista UPG 100 — Liste di preghiera e poster",
"upg100WallTag": "Mobilitazione alla preghiera",
"upg100WallIntro": "Distribuisci il compito della preghiera in gruppi di cinquanta e di cento — come Gesù sfamò i 5.000 (Luca 9:14). Ogni poster è una lista di 100 che puoi pregare, stampare e appendere al muro — pregando per loro <strong>con il nome del loro popolo</strong>."
"upg100WallIntro": "Distribuisci il compito della preghiera in gruppi di cinquanta e di cento — come Gesù sfamò i 5.000 (Luca 9:14). Ogni poster è una lista di 100 che puoi pregare, stampare e appendere al muro — pregando per loro <strong>con il nome del loro popolo</strong>.",
"loadingNamed": "Caricamento di {name}…",
"couldNotLoadNamed": "Impossibile caricare {name}."
}

View file

@ -15,5 +15,8 @@
"scale_bar": "Barra della scala",
"north_arrow": "Freccia nord",
"footer_attribution": "Piè di pagina (attribuzione obbligatoria)",
"cancel": "Annulla"
"cancel": "Annulla",
"map_preview": "anteprima della mappa",
"loading_snapshot": "Caricamento dell'istantanea della mappa…",
"preview_unavailable": "anteprima non disponibile"
}

View file

@ -72,5 +72,9 @@
},
"noDataAtThisLevel": "Sem dados neste nível",
"exportButton": "Exportar",
"exportSelection": "Exportar a seleção"
"exportSelection": "Exportar a seleção",
"loadingRows": "Carregando {label}…",
"loadingData": "Carregando dados…",
"showingTop": "Mostrando os {n} principais de {m}",
"useGeocoderHint": "use a busca acima para encontrar os restantes"
}

View file

@ -5,5 +5,7 @@
},
"upg100WallHeading": "A minha lista UPG 100 — Listas de oração e cartazes",
"upg100WallTag": "Mobilização de oração",
"upg100WallIntro": "Reparta a tarefa da oração em grupos de cinquenta e de cem — como Jesus alimentou os 5.000 (Lucas 9:14). Cada cartaz é uma lista de 100 que pode orar, imprimir e pôr na parede — orando por eles <strong>pelo nome do seu povo</strong>."
"upg100WallIntro": "Reparta a tarefa da oração em grupos de cinquenta e de cem — como Jesus alimentou os 5.000 (Lucas 9:14). Cada cartaz é uma lista de 100 que pode orar, imprimir e pôr na parede — orando por eles <strong>pelo nome do seu povo</strong>.",
"loadingNamed": "Carregando {name}…",
"couldNotLoadNamed": "Não foi possível carregar {name}."
}

View file

@ -15,5 +15,8 @@
"scale_bar": "Barra de escala",
"north_arrow": "Seta norte",
"footer_attribution": "Rodapé (atribuição obrigatória)",
"cancel": "Cancelar"
"cancel": "Cancelar",
"map_preview": "pré-visualização do mapa",
"loading_snapshot": "Carregando o instantâneo do mapa…",
"preview_unavailable": "pré-visualização indisponível"
}

View file

@ -72,5 +72,9 @@
},
"noDataAtThisLevel": "Nu există date la acest nivel",
"exportButton": "Exportă",
"exportSelection": "Exportă selecția"
"exportSelection": "Exportă selecția",
"loadingRows": "Se încarcă {label}…",
"loadingData": "Se încarcă datele…",
"showingTop": "Se afișează primele {n} din {m}",
"useGeocoderHint": "folosește căutarea de mai sus pentru a le găsi pe celelalte"
}

View file

@ -5,5 +5,7 @@
},
"upg100WallHeading": "Lista mea UPG 100 — Liste de rugăciune și afișe",
"upg100WallTag": "Mobilizare pentru rugăciune",
"upg100WallIntro": "Împarte sarcina rugăciunii în grupuri de câte cincizeci și o sută — așa cum Isus a hrănit cei 5.000 (Luca 9:14). Fiecare afiș este o listă de 100 pe care o poți ruga, tipări și pune pe perete — rugându-te pentru ei <strong>pe numele poporului lor</strong>."
"upg100WallIntro": "Împarte sarcina rugăciunii în grupuri de câte cincizeci și o sută — așa cum Isus a hrănit cei 5.000 (Luca 9:14). Fiecare afiș este o listă de 100 pe care o poți ruga, tipări și pune pe perete — rugându-te pentru ei <strong>pe numele poporului lor</strong>.",
"loadingNamed": "Se încarcă {name}…",
"couldNotLoadNamed": "Nu s-a putut încărca {name}."
}

View file

@ -15,5 +15,8 @@
"scale_bar": "Bară de scară",
"north_arrow": "Săgeata nord",
"footer_attribution": "Subsol (atribuire obligatorie)",
"cancel": "Anulează"
"cancel": "Anulează",
"map_preview": "previzualizare hartă",
"loading_snapshot": "Se încarcă instantaneul hărții…",
"preview_unavailable": "previzualizare indisponibilă"
}

View file

@ -72,5 +72,9 @@
},
"noDataAtThisLevel": "На этом уровне нет данных",
"exportButton": "Экспорт",
"exportSelection": "Экспортировать выбранное"
"exportSelection": "Экспортировать выбранное",
"loadingRows": "Загрузка {label}…",
"loadingData": "Загрузка данных…",
"showingTop": "Показаны первые {n} из {m}",
"useGeocoderHint": "используйте поиск выше, чтобы найти остальные"
}

View file

@ -5,5 +5,7 @@
},
"upg100WallHeading": "Мой список UPG 100 — молитвенные списки и плакаты",
"upg100WallTag": "Молитвенная мобилизация",
"upg100WallIntro": "Разделите молитвенный труд на группы по пятьдесят и по сто — так Иисус накормил 5000 (Луки 9:14). Каждый плакат — это список из 100, о котором можно молиться, распечатать и повесить на стену, молясь о них <strong>по имени их народа</strong>."
"upg100WallIntro": "Разделите молитвенный труд на группы по пятьдесят и по сто — так Иисус накормил 5000 (Луки 9:14). Каждый плакат — это список из 100, о котором можно молиться, распечатать и повесить на стену, молясь о них <strong>по имени их народа</strong>.",
"loadingNamed": "Загрузка {name}…",
"couldNotLoadNamed": "Не удалось загрузить {name}."
}

View file

@ -15,5 +15,8 @@
"scale_bar": "Масштабная линейка",
"north_arrow": "Указатель севера",
"footer_attribution": "Нижний колонтитул (указание источника обязательно)",
"cancel": "Отмена"
"cancel": "Отмена",
"map_preview": "предпросмотр карты",
"loading_snapshot": "Загрузка снимка карты…",
"preview_unavailable": "предпросмотр недоступен"
}

View file

@ -72,5 +72,9 @@
},
"noDataAtThisLevel": "此层级没有数据",
"exportButton": "导出",
"exportSelection": "导出所选内容"
"exportSelection": "导出所选内容",
"loadingRows": "正在加载{label}…",
"loadingData": "正在加载数据…",
"showingTop": "显示 {m} 个中的前 {n} 个",
"useGeocoderHint": "使用上方的搜索框查找其余内容"
}

View file

@ -5,5 +5,7 @@
},
"upg100WallHeading": "我的 UPG 100 名單 — 禱告名單與海報",
"upg100WallTag": "禱告動員",
"upg100WallIntro": "將禱告的工作分成五十人、一百人一組 — 就像耶穌餵飽五千人那樣(路加福音 9:14)。每一張海報都是一份 100 人名單,你可以為他們禱告、列印出來、貼在牆上 — <strong>按著他們族群的名字</strong>為他們禱告。"
"upg100WallIntro": "將禱告的工作分成五十人、一百人一組 — 就像耶穌餵飽五千人那樣(路加福音 9:14)。每一張海報都是一份 100 人名單,你可以為他們禱告、列印出來、貼在牆上 — <strong>按著他們族群的名字</strong>為他們禱告。",
"loadingNamed": "正在加载{name}…",
"couldNotLoadNamed": "无法加载{name}。"
}

View file

@ -15,5 +15,8 @@
"scale_bar": "比例尺",
"north_arrow": "指北针",
"footer_attribution": "页脚(必须署名)",
"cancel": "取消"
"cancel": "取消",
"map_preview": "地图预览",
"loading_snapshot": "正在加载地图快照…",
"preview_unavailable": "预览不可用"
}

View file

@ -222,9 +222,9 @@
v-for="row in mountedElementRows" :key="row.key"
class="vtl-host" v-show="row.key === active" :hidden="row.key !== active">
<div class="vtl-host-slot" :ref="el => setHostSlot(row.key, el)"></div>
<div v-if="hostState[row.key] === 'loading'" class="vtl-host-hint">Loading {{ row.label }}…</div>
<div v-if="hostState[row.key] === 'loading'" class="vtl-host-hint">{{ T('messages.loadingNamed', 'Loading {name}…').replace('{name}', row.label) }}</div>
<div v-else-if="hostState[row.key] === 'error'" class="vtl-host-hint err">
Could not load {{ row.label }}.<br><small>{{ hostError[row.key] }}</small>
{{ T('messages.couldNotLoadNamed', 'Could not load {name}.').replace('{name}', row.label) }}<br><small>{{ hostError[row.key] }}</small>
</div>
</section>
<!-- Host-owned panes (the SPA/studio render their own here). Always available. -->
@ -237,6 +237,13 @@
<script>
import { defineComponent } from 'vue'
import { tStatic } from '../i18n/index.js'
// This layout's chrome, read out of the catalogue. tStatic and not useI18n: it is the root of a
// custom element, where useI18n() cannot resolve its injection and throws. Exposed from setup()
// rather than kept at module scope, because an options-API template can only see what the component
// returns — a module-level const would compile and then render nothing.
const tr = (key, en) => tStatic(key) || en
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
import { icon as localIcon } from '@map/layouts/app-wrapper-material/icons.js'
@ -555,6 +562,7 @@ export default defineComponent({
emits: ['update:active'],
setup() {
useShadowStyles(VTL_SHADOW_CSS, 'vertical-tabs-layout')
return { T: tr }
},
data() {
// _folderOverride: user toggles keyed by group key (true=open, false=closed). When a folder

View file

@ -183,12 +183,10 @@
],
"plugin-designs/pplr-data-maps/src/components/SemanticTreeLegend.vue": [
"Loading",
"Loading data…",
"Pop",
"Showing top",
"Sort by UPGs",
"UPGs",
"use the geocoder above to find the rest"
"UPGs"
],
"plugin-designs/pplr-data-maps/src/views/pplr-coverage.vue": [
"Avg locales / 17",
@ -356,24 +354,6 @@
],
"library/components/legends/LegendRows.vue": [
"Toggle children"
],
"library/components/legends/SemanticTreeLegend.vue": [
"Loading",
"Loading data…",
"Showing top",
"use the geocoder above to find the rest"
],
"library/components/poster/PosterPreview.vue": [
"Loading map snapshot…",
"map preview"
],
"library/components/poster/PosterWall.vue": [
"people groups ·",
"preview unavailable"
],
"library/layouts/VerticalTabsLayout.vue": [
"Could not load",
"Loading"
]
},
"hasKey": {
@ -414,8 +394,10 @@
"People Group"
],
"plugin-designs/pplr-data-maps/src/components/SemanticTreeLegend.vue": [
"Loading data…",
"No data at this level",
"Sort by Population"
"Sort by Population",
"use the geocoder above to find the rest"
],
"plugin-designs/pplr-data-maps/src/views/pplr-index.vue": [
"Overview"