diff --git a/internal/check-hardcoded-english.mjs b/internal/check-hardcoded-english.mjs index 820bc13b..bd1018e8 100644 --- a/internal/check-hardcoded-english.mjs +++ b/internal/check-hardcoded-english.mjs @@ -41,6 +41,7 @@ import { readFileSync, readdirSync, existsSync } from 'node:fs' import { join } from 'node:path' import { pathToFileURL } from 'node:url' +import { execSync } from 'node:child_process' import { parse } from '@vue/compiler-sfc' import * as babel from '@babel/parser' @@ -91,6 +92,22 @@ function localizedNames(code) { for (const m of code.matchAll(/\bconst\s+([A-Za-z_$][\w$]*)\s*=\s*(t|tf)\b\s*(?![(.])/g)) names.add(m[1]) return names } +// ★ A DOM QUERY ARGUMENT IS A SELECTOR, NOT PROSE — and a selector made of custom-element tags +// reads as English only because the tag names are English words. gospel found +// `querySelector("gospel-resources-dashboard, gospel-resources-map")` reported as user-visible text: +// the single-tag rule matched neither, because it is a comma-separated LIST. Exempting by CALL SITE +// is the right shape (as with developer strings) — the site is the fact, and no content rule can +// reliably tell a tag list from a sentence. +const DOM_QUERY = new Set([ + 'querySelector', 'querySelectorAll', 'closest', 'matches', 'getElementById', + 'getElementsByClassName', 'getElementsByTagName', 'getElementsByName', +]) +function isDomQuerySite(parent) { + return !!parent && parent.type === 'CallExpression' + && parent.callee?.type === 'MemberExpression' + && DOM_QUERY.has(parent.callee.property?.name || '') +} + // A literal that is an argument to one of these never reaches a reader, whatever it says. function isDeveloperSite(parent) { if (!parent) return false @@ -249,7 +266,7 @@ function scanScript(code, file, out) { // ★ DEVELOPER STRINGS ARE EXEMPT BY CALL SITE, NOT BY CONTENT (upg100). `throw new Error('…')` // and console.* never reach a reader, and judging them by their text means inventing a rule for // every sentence a developer might write. The SITE is the fact; the wording is not. - if (isDeveloperSite(parent)) return + if (isDeveloperSite(parent) || isDomQuerySite(parent)) return if (parent && /Import|Export/.test(parent.type)) return if (parent && parent.type === 'TaggedTemplateExpression') return if (guardedFallback(stack, names)) return @@ -376,6 +393,8 @@ const PLANTED_BAD = ` ` const XAREA_BAD = `const t = useOptionalT('regions') @@ -446,6 +467,13 @@ function selfTest() { const ltr = bad.filter((f) => f.deliberate) ok(ltr.some((f) => /Counted BY LANGUAGE/.test(f.text)), 'marks text inside dir="ltr" as DELIBERATE, not a leak') ok(ltr.some((f) => /Source: eBible/.test(f.text)), 'and INHERITS the marker down the subtree') + // ★ gospel's real case: an island SPLIT BY INTERPOLATIONS. Each run between two {{ }} is its own + // text node, so a check that only marked a whole-text island reported the pieces as separate + // leaks — and they nearly got translated. is the element they actually use. + const splitIsland = bad.filter((f) => /sign languages on this map|both a film/.test(f.text)) + ok(splitIsland.length > 0, 'sees the pieces of an interpolation-split island at all') + ok(splitIsland.every((f) => f.deliberate), + `every piece of an interpolation-split dir="ltr" island is DELIBERATE (got ${splitIsland.filter((f) => !f.deliberate).length} not marked)`) ok(bad.filter((f) => !f.deliberate).some((f) => f.text === 'Prayer Coverage'), 'while a leak OUTSIDE the island is still counted') ok(!good.some((f) => f.text === 'All Regions'), 'QUIET on a call through an ALIAS of t()') ok(!good.some((f) => f.text === 'Regions List'), 'QUIET on a te()-guarded fallback') @@ -455,6 +483,8 @@ function selfTest() { ok(!good.some((f) => /map engine did not load/.test(f.text)), 'QUIET on the "did t() return the key?" miss idiom (msg !== \'a.dotted.key\')') ok(!good.some((f) => /table not found/.test(f.text)), 'QUIET on a throw new Error(…) argument — exempt by CALL SITE') ok(!good.some((f) => /boundaries failed/.test(f.text)), 'QUIET on a console.warn(…) argument') + ok(!good.some((f) => /gospel-resources-dashboard|regions-dashboard, doxa/.test(f.text)), + 'QUIET on a DOM query selector, including a comma-separated list of custom-element tags') ok(!good.some((f) => /rd-card|example\.test|nu-latn|HTTP/.test(f.text)), 'QUIET on css, urls, locale extensions and developer error text') ok(good.length === 0, `QUIET overall on a correct file (got ${good.length}${good.length ? ': ' + JSON.stringify(good[0]) : ''})`) // The blind spot is PINNED, not merely mentioned: if single-word detection is ever added, this @@ -473,6 +503,15 @@ function selfTest() { return fails } +// Short HEAD sha plus a dirty marker, or 'unknown' outside a checkout. +function treeStamp() { + try { + const sha = execSync('git rev-parse --short HEAD', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim() + const dirty = execSync('git status --porcelain', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim() + return dirty ? `${sha}+dirty(${dirty.split('\n').length} file(s))` : sha + } catch { return 'unknown (not a git checkout)' } +} + // ── main ──────────────────────────────────────────────────────────────────────────────────────── // RUN ONLY WHEN INVOKED DIRECTLY. Without this guard, `import`ing the module to reuse scanSfc() runs // the whole production scan as a side effect — which is how I first "verified" it against sr's real @@ -497,7 +536,12 @@ try { const deliberate = findings.filter((f) => f.deliberate) const leaks = findings.filter((f) => !f.deliberate) -console.log(`\n ${dirs.length} production plugin(s), ${files} SFC(s) scanned`) +// ★ THE RUN NAMES ITS OWN SHA. Three reports today were true of an older artifact than the one in +// hand, and each cost someone a wasted investigation — the last one nearly had a working island +// check "fixed". gospel's corollary is that the reader cannot check what the writer did not say, so +// the tool says it: any number quoted from this output carries the tree it was measured on, and a +// dirty tree is marked because then the sha alone does not describe what ran. +console.log(`\n ${dirs.length} production plugin(s), ${files} SFC(s) scanned · tree ${treeStamp()}`) for (const f of leaks) console.log(` ${f.kind.padEnd(16)} ${f.file.replace(/^.*plugin-designs\//, '')} ${JSON.stringify(f.text).slice(0, 90)}`) if (deliberate.length) { // Listed so nobody wonders where they went, but NOT counted: dir="ltr" is the plugin's own marker diff --git a/library/i18n/locales/ar/gospel.json b/library/i18n/locales/ar/gospel.json index 0a87a2ec..cd903b65 100644 --- a/library/i18n/locales/ar/gospel.json +++ b/library/i18n/locales/ar/gospel.json @@ -11,7 +11,8 @@ "placeholder": "ابحث عن شعب أو لغة أو رمز ISO", "clear": "مسح البحث", "none": "لا شيء يطابق هذا الاسم أو اللغة أو الرمز.", - "placeholderShort": "شعب أو لغة أو رمز ISO" + "placeholderShort": "شعب أو لغة أو رمز ISO", + "showingOf": "يُعرض {shown} من أصل {total} نتيجة — واصل الكتابة لتضييق البحث." }, "legend": { "title": "شعوب غير مبلَّغة ولا يوجد لها أي مورد إنجيلي", @@ -66,7 +67,8 @@ "haveAudio": "لديهم كتاب مقدس مسموع", "signLanguages": "لغات الإشارة", "growingUnreached": "متزايدون وغير مبلَّغين", - "deathsDay": "الوفيات المقدَّرة يوميًا" + "deathsDay": "الوفيات المقدَّرة يوميًا", + "alreadyPraying": "يصلّون بالفعل: {n}" }, "sheet": { "ownLanguage": "بلغتهم الأم", @@ -92,7 +94,17 @@ "audioScripture": "الكتاب المقدس الصوتي", "bibleStoriesPlain": "قصص الكتاب المقدس", "nParts": "عدد الأجزاء: {n}", - "zumeInLanguage": "Zume، باللغة {language}" + "zumeInLanguage": "Zume، باللغة {language}", + "jesusFilmTitle": "يفتح فيلم يسوع بهذه اللغة على jesusfilm.org.", + "sunSigned": "SUN (بلغة الإشارة)", + "readOnline": "اقرأ على الإنترنت", + "studyNotes": "ملاحظات دراسية", + "seeSite": "انظر الموقع", + "openPrayerPage": "افتح صفحة الصلاة لأجل {name} على doxa.life.", + "everyResourceIn": "كل الموارد المستخدمة في {country}", + "macroNote": "يصنّف معيار ISO 639-3 هذا ضمن اللغة الكبرى {macro} مع {lang}، التي تتوفر لها موارد. هكذا يجمع المعيار الرموز، وليس ادعاءً بأنها مفهومة بشكل متبادل.", + "videoCovers": "يغطي الفيديو: {covers}. هذا فيديو من الكتاب المقدس، وليس فيلم يسوع.", + "bibleVideoTitle": "{name} — فيديو سرد من الكتاب المقدس. هذا ليس فيلم يسوع. يغطي: {covers}" }, "state": { "noneFound": "لا شيء", @@ -103,7 +115,10 @@ "unchecked": "لم يُتحقق منه", "linkDead": "رابط معطّل", "wrongLanguage": "لغة غير صحيحة", - "varietyNotStated": "(اللهجة غير محددة)" + "varietyNotStated": "(اللهجة غير محددة)", + "loading": "جارٍ التحميل", + "linkNotChecked": "لم يُتحقَّق من هذا الرابط.", + "noCard": "لا توجد بطاقة لـ \"{slug}\"." }, "guide": { "dotVsCountry": "كل نقطة هي شعب واحد من شعوب دوكسا ولغته؛ أما لون البلد فهو عدد تلك الشعوب.", @@ -113,7 +128,8 @@ "isSignLanguage": "هذه لغة إشارة.", "wayInSome": "{n} من أصل {total}", "wayInAll": "كل واحدة من هذه الـ {n}", - "wayInWhat": "لديها مدخل — لغة جسرية، أو SUN لمجموعة صُمّ. اضغط على واحدة لرؤيته." + "wayInWhat": "لديها مدخل — لغة جسرية، أو SUN لمجموعة صُمّ. اضغط على واحدة لرؤيته.", + "numberMadeOf": "مِمَّ يتكوّن هذا الرقم." }, "tables": { "title": "موارد الإنجيل — البيانات", @@ -135,7 +151,9 @@ "t_resources": "الموارد حسب الشعب", "t_languages": "اللغات حسب البلد", "t_growth": "الوصول مقابل نمو السكان", - "t_bridgereach": "لغة واحدة، كم شعبًا؟" + "t_bridgereach": "لغة واحدة، كم شعبًا؟", + "growingHeroLead": "عدد الشعوب المتنامية: {count}", + "growingHeroTail": "{population} نسمة، بنحو {deaths} حالة وفاة يوميًا." }, "pages": { "listTitle": "موارد الإنجيل حسب اللغة", @@ -317,5 +335,38 @@ "c3Title": "البلدان الأكثر لغاتٍ غير مخدومة", "c3Sub": "البلدان الـ{n} الأكثر لغاتٍ لم يُعثر لها على شيء. طول العمود هو كل لغة موجودة في ذلك البلد.", "c3Unit": "لغات موجودة في البلد (اللغة العابرة للحدود تُحتسب في كل بلد)" + }, + "filter": { + "showingOnly": "يُعرض فقط: {what}.", + "only": { + "none": "المجموعات التي لم يُعثر لها على شيء", + "some": "المجموعات التي لديها مورد واحد على الأقل", + "hnr": "المجموعات التي يمكنها سماع الإنجيل لكن ليس لديها كتاب مقدس مكتوب في المصادر التي فُحصت", + "both": "المجموعات التي لديها كتاب مقدس وفيلم" + }, + "showAll": "عرض الكل ({count})", + "seeLargest": "عرض أكبر {count} ←" + }, + "gapList": { + "title": { + "groups": "أكبر المجموعات التي لم يُعثر لها على شيء", + "noprayer": "لم يُعثر على شيء، ولا أحد يصلّي", + "hnr": "يمكنهم سماع الإنجيل، ولا نص للقراءة", + "other": "أين تتركّز الفجوة" + }, + "largestTap": "هذه أكبر {count}؛ انقر على واحدة للانتقال إليها.", + "relatedLabel": "ذو صلة:", + "relatedHasResources": "ذو صلة: تتوفر موارد لـ {language}" + }, + "province": { + "largestWithNothing": "الأكبر بلا أي شيء: {examples}", + "wayIn": "المدخل: {ways} — {basis}.", + "basisCountryLevel": "على مستوى الدولة" + }, + "related": { + "nothingInItself": "لا شيء بلغة {language} نفسها" + }, + "deaf": { + "sunTaughtIn": "تُدرِّس {organisation} منهج SUN بالفعل في {countries}." } } diff --git a/library/i18n/locales/ar/messages.json b/library/i18n/locales/ar/messages.json index 14f8ab1f..cd2cc558 100644 --- a/library/i18n/locales/ar/messages.json +++ b/library/i18n/locales/ar/messages.json @@ -21,5 +21,12 @@ "engineNotLoaded": "لم يتم تحميل محرك الخريطة", "engineNotLoadedDetail": "لم يتم تحميل محرك الخريطة {engine}. القائمة ووسيلة الإيضاح غير متأثرتين.", "viewLoadFailed": "تعذّر تحميل هذا العرض.", - "retry": "إعادة المحاولة" + "retry": "إعادة المحاولة", + "noMapboxToken": "لا يوجد رمز Mapbox", + "noMapboxTokenDetail": "لم يُضبط أي رمز Mapbox. القائمة ووسيلة الإيضاح غير متأثرتين.", + "loadingBoundaries": "جارٍ تحميل الحدود…", + "fetchingUupgs": "جارٍ جلب {n} من UUPG…", + "mapStatusCounts": "المساحات: {polys} · المناطق: {zones}/{total} · الدبابيس: {pins}", + "tapTerritoriesHint": "انقر على الأقاليم في الخريطة أو صفوف وسيلة الإيضاح لإضافتها · انقر مجددًا لإزالتها", + "territoriesSelected": "الأقاليم: {count}" } diff --git a/library/i18n/locales/ar/poster.json b/library/i18n/locales/ar/poster.json index 4713222c..4c3ef782 100644 --- a/library/i18n/locales/ar/poster.json +++ b/library/i18n/locales/ar/poster.json @@ -18,5 +18,10 @@ "cancel": "إلغاء", "map_preview": "معاينة الخريطة", "loading_snapshot": "جارٍ تحميل لقطة الخريطة…", - "preview_unavailable": "المعاينة غير متاحة" + "preview_unavailable": "المعاينة غير متاحة", + "region_africa": "أفريقيا", + "region_middle_east": "الشرق الأوسط", + "region_central_asia": "آسيا الوسطى", + "region_southeast_asia_oceania": "جنوب شرق آسيا + أوقيانوسيا", + "region_china_mongolia": "الصين + منغوليا" } diff --git a/library/i18n/locales/de/gospel.json b/library/i18n/locales/de/gospel.json index 8dc8f3f4..fb175fb9 100644 --- a/library/i18n/locales/de/gospel.json +++ b/library/i18n/locales/de/gospel.json @@ -11,7 +11,8 @@ "placeholder": "Volksgruppe, Sprache oder ISO-Code suchen", "clear": "Suche löschen", "none": "Nichts passt zu diesem Namen, dieser Sprache oder diesem Code.", - "placeholderShort": "Gruppe, Sprache oder ISO-Code" + "placeholderShort": "Gruppe, Sprache oder ISO-Code", + "showingOf": "{shown} von {total} Treffern angezeigt — weitertippen, um einzugrenzen." }, "legend": { "title": "Unerreichte Volksgruppen ohne gefundene Evangeliums-Ressource", @@ -66,7 +67,8 @@ "haveAudio": "Haben Hörbibel", "signLanguages": "Gebärdensprachen", "growingUnreached": "Wachsend & unerreicht", - "deathsDay": "Geschätzte Todesfälle/Tag" + "deathsDay": "Geschätzte Todesfälle/Tag", + "alreadyPraying": "Beten bereits: {n}" }, "sheet": { "ownLanguage": "In ihrer eigenen Sprache", @@ -92,7 +94,17 @@ "audioScripture": "Audio-Bibeltext", "bibleStoriesPlain": "Bibelgeschichten", "nParts": "{n} Teile", - "zumeInLanguage": "Zume, auf {language}" + "zumeInLanguage": "Zume, auf {language}", + "jesusFilmTitle": "Öffnet den JESUS-Film in dieser Sprache auf jesusfilm.org.", + "sunSigned": "SUN (Gebärdensprache)", + "readOnline": "online lesen", + "studyNotes": "Studienanmerkungen", + "seeSite": "siehe Website", + "openPrayerPage": "Die Gebetsseite für {name} auf doxa.life öffnen.", + "everyResourceIn": "Alle in {country} genutzten Ressourcen", + "macroNote": "ISO 639-3 ordnet dies der Makrosprache {macro} zu, zusammen mit {lang}, für die es Ressourcen gibt. So gruppiert der Standard die Codes; das ist keine Aussage über gegenseitige Verständlichkeit.", + "videoCovers": "Das Video umfasst: {covers}. Das ist Bibelvideo, nicht der JESUS-Film.", + "bibleVideoTitle": "{name} — Bibel-Erzählvideo. Das ist NICHT der JESUS-Film. Umfasst: {covers}" }, "state": { "noneFound": "nichts gefunden", @@ -103,7 +115,10 @@ "unchecked": "ungeprüft", "linkDead": "Link tot", "wrongLanguage": "falsche Sprache", - "varietyNotStated": "(Varietät nicht angegeben)" + "varietyNotStated": "(Varietät nicht angegeben)", + "loading": "Wird geladen", + "linkNotChecked": "Dieser Link wurde nicht geprüft.", + "noCard": "Keine Karte für \"{slug}\"." }, "guide": { "dotVsCountry": "Ein Punkt ist EINE Volksgruppe von Doxa mit ihrer eigenen Sprache; ein Land ist die ANZAHL dieser Gruppen.", @@ -113,7 +128,8 @@ "isSignLanguage": "Dies ist eine Gebärdensprache.", "wayInSome": "{n} von diesen {total}", "wayInAll": "Jede dieser {n}", - "wayInWhat": "hat einen Zugang – eine Brückensprache oder SUN für eine gehörlose Gruppe. Tippe auf eine, um ihn zu sehen." + "wayInWhat": "hat einen Zugang – eine Brückensprache oder SUN für eine gehörlose Gruppe. Tippe auf eine, um ihn zu sehen.", + "numberMadeOf": "Woraus sich diese Zahl zusammensetzt." }, "tables": { "title": "Evangeliums-Ressourcen — Daten", @@ -135,7 +151,9 @@ "t_resources": "Ressourcen nach Volksgruppe", "t_languages": "Sprachen nach Land", "t_growth": "Zugang vs. Bevölkerungswachstum", - "t_bridgereach": "Eine Sprache, wie viele Völker" + "t_bridgereach": "Eine Sprache, wie viele Völker", + "growingHeroLead": "{count} Volksgruppen wachsen", + "growingHeroTail": "{population} Menschen, geschätzt {deaths} Todesfälle pro Tag." }, "pages": { "listTitle": "Evangeliums-Ressourcen nach Sprache", @@ -317,5 +335,38 @@ "c3Title": "Länder mit den meisten unversorgten Sprachen", "c3Sub": "Die {n} Länder mit den meisten Sprachen, für die nichts gefunden wurde. Die Balkenlänge ist jede im Land vorhandene Sprache.", "c3Unit": "im Land vorhandene Sprachen (eine grenzüberschreitende Sprache zählt in jedem Land)" + }, + "filter": { + "showingOnly": "Nur {what} werden angezeigt.", + "only": { + "none": "Gruppen, für die nichts gefunden wurde", + "some": "Gruppen mit mindestens einer Ressource", + "hnr": "Gruppen, die das Evangelium hören können, aber in den geprüften Quellen keine geschriebene Schrift haben", + "both": "Gruppen mit Schrift und Film" + }, + "showAll": "Alle {count} anzeigen", + "seeLargest": "Die {count} größten ansehen →" + }, + "gapList": { + "title": { + "groups": "Größte Gruppen, für die nichts gefunden wurde", + "noprayer": "Nichts gefunden, und niemand betet", + "hnr": "Können das Evangelium hören, kein Text zum Lesen", + "other": "Wo sich die Lücke konzentriert" + }, + "largestTap": "Das sind die {count} größten; tippen Sie auf eine, um dorthin zu fliegen.", + "relatedLabel": "verwandt:", + "relatedHasResources": "verwandt: {language} hat Ressourcen" + }, + "province": { + "largestWithNothing": "Größte ohne alles: {examples}", + "wayIn": "Zugang: {ways} — {basis}.", + "basisCountryLevel": "auf Landesebene" + }, + "related": { + "nothingInItself": "Nichts in {language} selbst" + }, + "deaf": { + "sunTaughtIn": "SUN wird in {countries} bereits von {organisation} unterrichtet." } } diff --git a/library/i18n/locales/de/messages.json b/library/i18n/locales/de/messages.json index 90f550bc..05384ffe 100644 --- a/library/i18n/locales/de/messages.json +++ b/library/i18n/locales/de/messages.json @@ -21,5 +21,12 @@ "engineNotLoaded": "Karten-Engine nicht geladen", "engineNotLoadedDetail": "Die Karten-Engine {engine} wurde nicht geladen. Liste und Legende sind nicht betroffen.", "viewLoadFailed": "Diese Ansicht konnte nicht geladen werden.", - "retry": "Erneut versuchen" + "retry": "Erneut versuchen", + "noMapboxToken": "Kein Mapbox-Token", + "noMapboxTokenDetail": "Kein Mapbox-Token konfiguriert. Liste und Legende sind nicht betroffen.", + "loadingBoundaries": "Grenzen werden geladen…", + "fetchingUupgs": "{n} UUPGs werden abgerufen…", + "mapStatusCounts": "Flächen: {polys} · Zonen: {zones}/{total} · Pins: {pins}", + "tapTerritoriesHint": "Tippen Sie auf Gebiete auf der Karte oder auf Legendenzeilen, um sie hinzuzufügen · erneut tippen zum Entfernen", + "territoriesSelected": "Gebiete: {count}" } diff --git a/library/i18n/locales/de/poster.json b/library/i18n/locales/de/poster.json index 120eece6..19b1881d 100644 --- a/library/i18n/locales/de/poster.json +++ b/library/i18n/locales/de/poster.json @@ -18,5 +18,10 @@ "cancel": "Abbrechen", "map_preview": "Kartenvorschau", "loading_snapshot": "Kartenaufnahme wird geladen…", - "preview_unavailable": "Vorschau nicht verfügbar" + "preview_unavailable": "Vorschau nicht verfügbar", + "region_africa": "Afrika", + "region_middle_east": "Naher Osten", + "region_central_asia": "Zentralasien", + "region_southeast_asia_oceania": "Südostasien + Ozeanien", + "region_china_mongolia": "China + Mongolei" } diff --git a/library/i18n/locales/en/gospel.json b/library/i18n/locales/en/gospel.json index d8af3a29..e1e7d565 100644 --- a/library/i18n/locales/en/gospel.json +++ b/library/i18n/locales/en/gospel.json @@ -11,7 +11,8 @@ "placeholder": "Find a people group, language or ISO code", "clear": "Clear search", "none": "Nothing matches that name, language or code.", - "placeholderShort": "Group, language or ISO code" + "placeholderShort": "Group, language or ISO code", + "showingOf": "Showing {shown} of {total} matches — keep typing to narrow." }, "legend": { "title": "Unengaged groups with no gospel resource found", @@ -66,7 +67,8 @@ "haveAudio": "Have audio Scripture", "signLanguages": "Sign languages", "growingUnreached": "Growing & unreached", - "deathsDay": "Est. deaths/day" + "deathsDay": "Est. deaths/day", + "alreadyPraying": "Already praying: {n}" }, "sheet": { "ownLanguage": "In their own language", @@ -92,7 +94,17 @@ "audioScripture": "Audio Scripture", "bibleStoriesPlain": "Bible Stories", "nParts": "{n} parts", - "zumeInLanguage": "Zume, in {language}" + "zumeInLanguage": "Zume, in {language}", + "jesusFilmTitle": "Opens the JESUS Film in this language on jesusfilm.org.", + "sunSigned": "SUN (signed)", + "readOnline": "read online", + "studyNotes": "study notes", + "seeSite": "see site", + "openPrayerPage": "Open the prayer page for {name} on doxa.life.", + "everyResourceIn": "Every resource used in {country}", + "macroNote": "ISO 639-3 files this under macrolanguage {macro} with {lang}, which has resources. That is how the standard groups the codes, not a claim they are mutually intelligible.", + "videoCovers": "Video covers: {covers}. That is Bible video, not the JESUS Film.", + "bibleVideoTitle": "{name} — Bible narrative video. This is NOT the JESUS Film. Covers: {covers}" }, "state": { "noneFound": "none found", @@ -103,7 +115,10 @@ "unchecked": "unchecked", "linkDead": "link dead", "wrongLanguage": "wrong language", - "varietyNotStated": "(variety not stated)" + "varietyNotStated": "(variety not stated)", + "loading": "Loading", + "linkNotChecked": "This link has not been checked.", + "noCard": "No card for \"{slug}\"." }, "guide": { "dotVsCountry": "A dot is ONE of Doxa's people groups and its own language; a country is a COUNT of those groups.", @@ -113,7 +128,8 @@ "isSignLanguage": "This is a sign language.", "wayInSome": "{n} of those {total}", "wayInAll": "Every one of those {n}", - "wayInWhat": "has a way in — a bridge language, or SUN for a Deaf group. Tap one to see it." + "wayInWhat": "has a way in — a bridge language, or SUN for a Deaf group. Tap one to see it.", + "numberMadeOf": "What that number is made of." }, "tables": { "title": "Gospel Resources — Data", @@ -135,7 +151,9 @@ "t_resources": "Resources by People Group", "t_languages": "Languages by Country", "t_growth": "Access vs Population Growth", - "t_bridgereach": "One Language, How Many Peoples" + "t_bridgereach": "One Language, How Many Peoples", + "growingHeroLead": "{count} people groups are growing", + "growingHeroTail": "{population} people, an estimated {deaths} deaths per day." }, "pages": { "listTitle": "Gospel Resources by Language", @@ -317,5 +335,38 @@ "c3Title": "Countries with the most unserved languages", "c3Sub": "The {n} countries with the most languages that have nothing found. Bar length is every language present in that country.", "c3Unit": "languages present in the country (a cross-border language counts in each)" + }, + "filter": { + "showingOnly": "Showing only {what}.", + "only": { + "none": "groups with nothing found", + "some": "groups with some resource", + "hnr": "groups that can hear the gospel but have no text Scripture in the pools checked", + "both": "groups with Scripture and film" + }, + "showAll": "Show all {count}", + "seeLargest": "See the {count} largest →" + }, + "gapList": { + "title": { + "groups": "Largest groups with nothing found", + "noprayer": "Nothing found, and nobody praying", + "hnr": "Can hear the gospel, no text to read", + "other": "Where the gap concentrates" + }, + "largestTap": "These are the {count} largest; tap one to fly there.", + "relatedLabel": "related:", + "relatedHasResources": "related: {language} has resources" + }, + "province": { + "largestWithNothing": "Largest with nothing: {examples}", + "wayIn": "Way in: {ways} — {basis}.", + "basisCountryLevel": "country-level" + }, + "related": { + "nothingInItself": "Nothing in {language} itself" + }, + "deaf": { + "sunTaughtIn": "SUN is already being taught in {countries} by {organisation}." } } diff --git a/library/i18n/locales/en/messages.json b/library/i18n/locales/en/messages.json index 3d11cff0..543f9017 100644 --- a/library/i18n/locales/en/messages.json +++ b/library/i18n/locales/en/messages.json @@ -21,5 +21,12 @@ "engineNotLoaded": "map engine not loaded", "engineNotLoadedDetail": "The {engine} map engine did not load. The list and legend are unaffected.", "viewLoadFailed": "This view couldn't load.", - "retry": "Retry" + "retry": "Retry", + "noMapboxToken": "No Mapbox token", + "noMapboxTokenDetail": "No Mapbox token configured. The list and legend are unaffected.", + "loadingBoundaries": "Loading boundaries…", + "fetchingUupgs": "Fetching {n} UUPGs…", + "mapStatusCounts": "Areas: {polys} · zones: {zones}/{total} · pins: {pins}", + "tapTerritoriesHint": "Tap territories on the map or legend rows to add · tap again to remove", + "territoriesSelected": "Territories: {count}" } diff --git a/library/i18n/locales/en/poster.json b/library/i18n/locales/en/poster.json index b7fe6276..fea370a9 100644 --- a/library/i18n/locales/en/poster.json +++ b/library/i18n/locales/en/poster.json @@ -18,5 +18,10 @@ "cancel": "Cancel", "map_preview": "map preview", "loading_snapshot": "Loading map snapshot…", - "preview_unavailable": "preview unavailable" + "preview_unavailable": "preview unavailable", + "region_africa": "Africa", + "region_middle_east": "Middle East", + "region_central_asia": "Central Asia", + "region_southeast_asia_oceania": "Southeast Asia + Oceania", + "region_china_mongolia": "China + Mongolia" } diff --git a/library/i18n/locales/es/gospel.json b/library/i18n/locales/es/gospel.json index a6c9b7b3..6fc09f45 100644 --- a/library/i18n/locales/es/gospel.json +++ b/library/i18n/locales/es/gospel.json @@ -11,7 +11,8 @@ "placeholder": "Busca un pueblo, idioma o código ISO", "clear": "Borrar búsqueda", "none": "Nada coincide con ese nombre, idioma o código.", - "placeholderShort": "Pueblo, idioma o código ISO" + "placeholderShort": "Pueblo, idioma o código ISO", + "showingOf": "Se muestran {shown} de {total} coincidencias; siga escribiendo para acotar." }, "legend": { "title": "Pueblos no alcanzados sin ningún recurso del evangelio", @@ -66,7 +67,8 @@ "haveAudio": "Tienen Escrituras en audio", "signLanguages": "Lenguas de señas", "growingUnreached": "En crecimiento y no alcanzados", - "deathsDay": "Muertes estimadas/día" + "deathsDay": "Muertes estimadas/día", + "alreadyPraying": "Ya orando: {n}" }, "sheet": { "ownLanguage": "En su propio idioma", @@ -92,7 +94,17 @@ "audioScripture": "Escrituras en audio", "bibleStoriesPlain": "Historias bíblicas", "nParts": "{n} partes", - "zumeInLanguage": "Zume, en {language}" + "zumeInLanguage": "Zume, en {language}", + "jesusFilmTitle": "Abre la película JESÚS en este idioma en jesusfilm.org.", + "sunSigned": "SUN (en lengua de señas)", + "readOnline": "leer en línea", + "studyNotes": "notas de estudio", + "seeSite": "ver el sitio", + "openPrayerPage": "Abrir la página de oración de {name} en doxa.life.", + "everyResourceIn": "Todos los recursos usados en {country}", + "macroNote": "ISO 639-3 clasifica esto bajo la macrolengua {macro} junto con {lang}, que tiene recursos. Así agrupa los códigos el estándar; no significa que sean mutuamente inteligibles.", + "videoCovers": "El video abarca: {covers}. Es video bíblico, no la película JESÚS.", + "bibleVideoTitle": "{name}: video de narración bíblica. NO es la película JESÚS. Abarca: {covers}" }, "state": { "noneFound": "nada encontrado", @@ -103,7 +115,10 @@ "unchecked": "sin verificar", "linkDead": "enlace roto", "wrongLanguage": "idioma incorrecto", - "varietyNotStated": "(variedad no indicada)" + "varietyNotStated": "(variedad no indicada)", + "loading": "Cargando", + "linkNotChecked": "Este enlace no se ha comprobado.", + "noCard": "No hay ficha para \"{slug}\"." }, "guide": { "dotVsCountry": "Cada punto es UN pueblo de Doxa y su propio idioma; un país es el NÚMERO de esos pueblos.", @@ -113,7 +128,8 @@ "isSignLanguage": "Esta es una lengua de señas.", "wayInSome": "{n} de esos {total}", "wayInAll": "Cada uno de esos {n}", - "wayInWhat": "tiene una vía de acceso: una lengua puente, o SUN para un grupo sordo. Toca uno para verla." + "wayInWhat": "tiene una vía de acceso: una lengua puente, o SUN para un grupo sordo. Toca uno para verla.", + "numberMadeOf": "De qué se compone esa cifra." }, "tables": { "title": "Recursos del evangelio — Datos", @@ -135,7 +151,9 @@ "t_resources": "Recursos por pueblo", "t_languages": "Idiomas por país", "t_growth": "Acceso vs crecimiento", - "t_bridgereach": "Una lengua, cuántos pueblos" + "t_bridgereach": "Una lengua, cuántos pueblos", + "growingHeroLead": "{count} pueblos están creciendo", + "growingHeroTail": "{population} personas, con un estimado de {deaths} muertes al día." }, "pages": { "listTitle": "Recursos del evangelio por idioma", @@ -317,5 +335,38 @@ "c3Title": "Países con más lenguas desatendidas", "c3Sub": "Los {n} países con más lenguas sin nada encontrado. La longitud de la barra es cada lengua presente en ese país.", "c3Unit": "lenguas presentes en el país (una lengua transfronteriza cuenta en cada uno)" + }, + "filter": { + "showingOnly": "Solo se muestran {what}.", + "only": { + "none": "grupos sin nada encontrado", + "some": "grupos con algún recurso", + "hnr": "grupos que pueden oír el evangelio pero no tienen Escritura escrita en las fuentes consultadas", + "both": "grupos con Escritura y película" + }, + "showAll": "Mostrar los {count}", + "seeLargest": "Ver los {count} más grandes →" + }, + "gapList": { + "title": { + "groups": "Grupos más grandes sin nada encontrado", + "noprayer": "Nada encontrado y nadie orando", + "hnr": "Pueden oír el evangelio, sin texto para leer", + "other": "Dónde se concentra la brecha" + }, + "largestTap": "Estos son los {count} más grandes; toque uno para ir allí.", + "relatedLabel": "relacionado:", + "relatedHasResources": "relacionado: {language} tiene recursos" + }, + "province": { + "largestWithNothing": "Los más grandes sin nada: {examples}", + "wayIn": "Vía de entrada: {ways} — {basis}.", + "basisCountryLevel": "a nivel de país" + }, + "related": { + "nothingInItself": "Nada en {language} en sí" + }, + "deaf": { + "sunTaughtIn": "{organisation} ya enseña SUN en {countries}." } } diff --git a/library/i18n/locales/es/messages.json b/library/i18n/locales/es/messages.json index 56d52442..afc4559d 100644 --- a/library/i18n/locales/es/messages.json +++ b/library/i18n/locales/es/messages.json @@ -21,5 +21,12 @@ "engineNotLoaded": "motor de mapas no cargado", "engineNotLoadedDetail": "El motor de mapas {engine} no se cargó. La lista y la leyenda no se ven afectadas.", "viewLoadFailed": "No se pudo cargar esta vista.", - "retry": "Reintentar" + "retry": "Reintentar", + "noMapboxToken": "No hay token de Mapbox", + "noMapboxTokenDetail": "No hay ningún token de Mapbox configurado. La lista y la leyenda no se ven afectadas.", + "loadingBoundaries": "Cargando límites…", + "fetchingUupgs": "Obteniendo {n} UUPG…", + "mapStatusCounts": "Áreas: {polys} · zonas: {zones}/{total} · marcadores: {pins}", + "tapTerritoriesHint": "Toque territorios en el mapa o filas de la leyenda para añadirlos · toque de nuevo para quitarlos", + "territoriesSelected": "Territorios: {count}" } diff --git a/library/i18n/locales/es/poster.json b/library/i18n/locales/es/poster.json index 2d307ed9..817b9386 100644 --- a/library/i18n/locales/es/poster.json +++ b/library/i18n/locales/es/poster.json @@ -18,5 +18,10 @@ "cancel": "Cancelar", "map_preview": "vista previa del mapa", "loading_snapshot": "Cargando la captura del mapa…", - "preview_unavailable": "vista previa no disponible" + "preview_unavailable": "vista previa no disponible", + "region_africa": "África", + "region_middle_east": "Oriente Medio", + "region_central_asia": "Asia Central", + "region_southeast_asia_oceania": "Sudeste Asiático + Oceanía", + "region_china_mongolia": "China + Mongolia" } diff --git a/library/i18n/locales/fr/gospel.json b/library/i18n/locales/fr/gospel.json index 03a2e360..8219b25a 100644 --- a/library/i18n/locales/fr/gospel.json +++ b/library/i18n/locales/fr/gospel.json @@ -11,7 +11,8 @@ "placeholder": "Chercher un peuple, une langue ou un code ISO", "clear": "Effacer la recherche", "none": "Rien ne correspond à ce nom, cette langue ou ce code.", - "placeholderShort": "Peuple, langue ou code ISO" + "placeholderShort": "Peuple, langue ou code ISO", + "showingOf": "{shown} résultats affichés sur {total} — continuez à taper pour affiner." }, "legend": { "title": "Peuples non atteints sans aucune ressource de l'Évangile", @@ -66,7 +67,8 @@ "haveAudio": "Ont les Écritures en audio", "signLanguages": "Langues des signes", "growingUnreached": "En croissance et non atteints", - "deathsDay": "Décès estimés/jour" + "deathsDay": "Décès estimés/jour", + "alreadyPraying": "Prient déjà : {n}" }, "sheet": { "ownLanguage": "Dans leur propre langue", @@ -92,7 +94,17 @@ "audioScripture": "Écritures en audio", "bibleStoriesPlain": "Histoires bibliques", "nParts": "{n} parties", - "zumeInLanguage": "Zume, en {language}" + "zumeInLanguage": "Zume, en {language}", + "jesusFilmTitle": "Ouvre le film JÉSUS dans cette langue sur jesusfilm.org.", + "sunSigned": "SUN (en langue des signes)", + "readOnline": "lire en ligne", + "studyNotes": "notes d’étude", + "seeSite": "voir le site", + "openPrayerPage": "Ouvrir la page de prière pour {name} sur doxa.life.", + "everyResourceIn": "Toutes les ressources utilisées : {country}", + "macroNote": "L’ISO 639-3 classe ceci sous la macrolangue {macro} avec {lang}, qui dispose de ressources. C’est ainsi que la norme regroupe les codes, pas une affirmation d’intercompréhension.", + "videoCovers": "La vidéo couvre : {covers}. C’est une vidéo biblique, pas le film JÉSUS.", + "bibleVideoTitle": "{name} — vidéo de récit biblique. Ce n’est PAS le film JÉSUS. Couvre : {covers}" }, "state": { "noneFound": "rien trouvé", @@ -103,7 +115,10 @@ "unchecked": "non vérifié", "linkDead": "lien mort", "wrongLanguage": "mauvaise langue", - "varietyNotStated": "(variété non précisée)" + "varietyNotStated": "(variété non précisée)", + "loading": "Chargement", + "linkNotChecked": "Ce lien n’a pas été vérifié.", + "noCard": "Aucune fiche pour « {slug} »." }, "guide": { "dotVsCountry": "Un point est UN peuple de Doxa et sa propre langue ; un pays est le NOMBRE de ces peuples.", @@ -113,7 +128,8 @@ "isSignLanguage": "Il s’agit d’une langue des signes.", "wayInSome": "{n} sur ces {total}", "wayInAll": "Chacun de ces {n}", - "wayInWhat": "a une porte d’entrée — une langue passerelle, ou SUN pour un groupe sourd. Touchez-en un pour la voir." + "wayInWhat": "a une porte d’entrée — une langue passerelle, ou SUN pour un groupe sourd. Touchez-en un pour la voir.", + "numberMadeOf": "Ce qui compose ce nombre." }, "tables": { "title": "Ressources de l'Évangile — Données", @@ -135,7 +151,9 @@ "t_resources": "Ressources par peuple", "t_languages": "Langues par pays", "t_growth": "Accès vs croissance", - "t_bridgereach": "Une langue, combien de peuples" + "t_bridgereach": "Une langue, combien de peuples", + "growingHeroLead": "{count} peuples sont en croissance", + "growingHeroTail": "{population} personnes, soit environ {deaths} décès par jour." }, "pages": { "listTitle": "Ressources de l'Évangile par langue", @@ -317,5 +335,38 @@ "c3Title": "Pays comptant le plus de langues non desservies", "c3Sub": "Les {n} pays comptant le plus de langues sans rien de trouvé. La longueur de la barre correspond à chaque langue présente dans ce pays.", "c3Unit": "langues présentes dans le pays (une langue transfrontalière compte dans chacun)" + }, + "filter": { + "showingOnly": "Affichage limité aux {what}.", + "only": { + "none": "groupes pour lesquels rien n’a été trouvé", + "some": "groupes disposant d’au moins une ressource", + "hnr": "groupes qui peuvent entendre l’Évangile mais n’ont aucune Écriture écrite dans les sources vérifiées", + "both": "groupes disposant de l’Écriture et du film" + }, + "showAll": "Afficher les {count}", + "seeLargest": "Voir les {count} plus grands →" + }, + "gapList": { + "title": { + "groups": "Plus grands groupes pour lesquels rien n’a été trouvé", + "noprayer": "Rien trouvé, et personne ne prie", + "hnr": "Peuvent entendre l’Évangile, aucun texte à lire", + "other": "Où se concentre le manque" + }, + "largestTap": "Voici les {count} plus grands ; touchez-en un pour vous y rendre.", + "relatedLabel": "apparenté :", + "relatedHasResources": "apparenté : {language} dispose de ressources" + }, + "province": { + "largestWithNothing": "Les plus grands sans rien : {examples}", + "wayIn": "Porte d’entrée : {ways} — {basis}.", + "basisCountryLevel": "au niveau du pays" + }, + "related": { + "nothingInItself": "Rien en {language} même" + }, + "deaf": { + "sunTaughtIn": "SUN est déjà enseigné par {organisation} dans : {countries}." } } diff --git a/library/i18n/locales/fr/messages.json b/library/i18n/locales/fr/messages.json index 20dc5ad6..673e60c6 100644 --- a/library/i18n/locales/fr/messages.json +++ b/library/i18n/locales/fr/messages.json @@ -21,5 +21,12 @@ "engineNotLoaded": "moteur cartographique non chargé", "engineNotLoadedDetail": "Le moteur cartographique {engine} ne s’est pas chargé. La liste et la légende ne sont pas affectées.", "viewLoadFailed": "Impossible de charger cette vue.", - "retry": "Réessayer" + "retry": "Réessayer", + "noMapboxToken": "Aucun jeton Mapbox", + "noMapboxTokenDetail": "Aucun jeton Mapbox n’est configuré. La liste et la légende ne sont pas affectées.", + "loadingBoundaries": "Chargement des limites…", + "fetchingUupgs": "Récupération de {n} UUPG…", + "mapStatusCounts": "Surfaces : {polys} · zones : {zones}/{total} · repères : {pins}", + "tapTerritoriesHint": "Touchez des territoires sur la carte ou des lignes de la légende pour les ajouter · touchez à nouveau pour les retirer", + "territoriesSelected": "Territoires : {count}" } diff --git a/library/i18n/locales/fr/poster.json b/library/i18n/locales/fr/poster.json index 598722ed..93580ef4 100644 --- a/library/i18n/locales/fr/poster.json +++ b/library/i18n/locales/fr/poster.json @@ -18,5 +18,10 @@ "cancel": "Annuler", "map_preview": "aperçu de la carte", "loading_snapshot": "Chargement de l'instantané de la carte…", - "preview_unavailable": "aperçu indisponible" + "preview_unavailable": "aperçu indisponible", + "region_africa": "Afrique", + "region_middle_east": "Moyen-Orient", + "region_central_asia": "Asie centrale", + "region_southeast_asia_oceania": "Asie du Sud-Est + Océanie", + "region_china_mongolia": "Chine + Mongolie" } diff --git a/library/i18n/locales/hi/gospel.json b/library/i18n/locales/hi/gospel.json index f03907d2..f2f614ee 100644 --- a/library/i18n/locales/hi/gospel.json +++ b/library/i18n/locales/hi/gospel.json @@ -11,7 +11,8 @@ "placeholder": "जनसमूह, भाषा या ISO कोड खोजें", "clear": "खोज साफ़ करें", "none": "इस नाम, भाषा या कोड से कुछ भी मेल नहीं खाता।", - "placeholderShort": "जनसमूह, भाषा या ISO कोड" + "placeholderShort": "जनसमूह, भाषा या ISO कोड", + "showingOf": "{total} मिलानों में से {shown} दिखाए जा रहे हैं — सीमित करने के लिए टाइप करते रहें।" }, "legend": { "title": "ऐसे जनसमूह जिन तक सुसमाचार नहीं पहुँचा और कोई संसाधन नहीं मिला", @@ -66,7 +67,8 @@ "haveAudio": "ऑडियो शास्त्र उपलब्ध", "signLanguages": "सांकेतिक भाषाएँ", "growingUnreached": "बढ़ते और अनपहुँचे", - "deathsDay": "अनुमानित मृत्यु/दिन" + "deathsDay": "अनुमानित मृत्यु/दिन", + "alreadyPraying": "पहले से प्रार्थना कर रहे: {n}" }, "sheet": { "ownLanguage": "उनकी अपनी भाषा में", @@ -92,7 +94,17 @@ "audioScripture": "ऑडियो पवित्र शास्त्र", "bibleStoriesPlain": "बाइबल की कहानियाँ", "nParts": "{n} भाग", - "zumeInLanguage": "Zume, {language} में" + "zumeInLanguage": "Zume, {language} में", + "jesusFilmTitle": "jesusfilm.org पर इस भाषा में यीशु फ़िल्म खोलता है।", + "sunSigned": "SUN (सांकेतिक भाषा)", + "readOnline": "ऑनलाइन पढ़ें", + "studyNotes": "अध्ययन टिप्पणियाँ", + "seeSite": "साइट देखें", + "openPrayerPage": "doxa.life पर {name} के लिए प्रार्थना पृष्ठ खोलें।", + "everyResourceIn": "{country} में उपयोग किए गए सभी संसाधन", + "macroNote": "ISO 639-3 इसे मैक्रोभाषा {macro} के अंतर्गत {lang} के साथ रखता है, जिसके संसाधन उपलब्ध हैं। मानक कोडों को इसी तरह समूहित करता है; इसका अर्थ यह नहीं कि वे परस्पर बोधगम्य हैं।", + "videoCovers": "वीडियो में शामिल: {covers}। यह बाइबल वीडियो है, यीशु फ़िल्म नहीं।", + "bibleVideoTitle": "{name} — बाइबल कथा वीडियो। यह यीशु फ़िल्म नहीं है। शामिल: {covers}" }, "state": { "noneFound": "कुछ नहीं मिला", @@ -103,7 +115,10 @@ "unchecked": "अनजाँचा", "linkDead": "लिंक निष्क्रिय", "wrongLanguage": "ग़लत भाषा", - "varietyNotStated": "(उपभाषा नहीं बताई गई)" + "varietyNotStated": "(उपभाषा नहीं बताई गई)", + "loading": "लोड हो रहा है", + "linkNotChecked": "इस लिंक की जाँच नहीं की गई है।", + "noCard": "\"{slug}\" के लिए कोई कार्ड नहीं।" }, "guide": { "dotVsCountry": "हर बिंदु दोक्सा का एक जनसमूह और उसकी अपनी भाषा है; देश का रंग उन जनसमूहों की संख्या है।", @@ -113,7 +128,8 @@ "isSignLanguage": "यह एक सांकेतिक भाषा है।", "wayInSome": "उन {total} में से {n}", "wayInAll": "उन {n} में से हर एक", - "wayInWhat": "के पास एक रास्ता है — एक सेतु भाषा, या बधिर समूह के लिए SUN। देखने के लिए किसी एक पर टैप करें।" + "wayInWhat": "के पास एक रास्ता है — एक सेतु भाषा, या बधिर समूह के लिए SUN। देखने के लिए किसी एक पर टैप करें।", + "numberMadeOf": "यह संख्या किससे बनी है।" }, "tables": { "title": "सुसमाचार संसाधन — डेटा", @@ -135,7 +151,9 @@ "t_resources": "जनसमूह अनुसार संसाधन", "t_languages": "देश अनुसार भाषाएँ", "t_growth": "पहुँच बनाम जनसंख्या वृद्धि", - "t_bridgereach": "एक भाषा, कितने जनसमूह" + "t_bridgereach": "एक भाषा, कितने जनसमूह", + "growingHeroLead": "{count} जनसमूह बढ़ रहे हैं", + "growingHeroTail": "{population} लोग, अनुमानित {deaths} मौतें प्रतिदिन।" }, "pages": { "listTitle": "भाषा अनुसार सुसमाचार संसाधन", @@ -317,5 +335,38 @@ "c3Title": "सबसे अधिक असेवित भाषाओं वाले देश", "c3Sub": "{n} देश जिनमें सबसे अधिक भाषाओं के लिए कुछ नहीं मिला। बार की लंबाई उस देश में मौजूद हर भाषा है।", "c3Unit": "देश में मौजूद भाषाएँ (सीमा-पार भाषा हर देश में गिनी जाती है)" + }, + "filter": { + "showingOnly": "केवल {what} दिखाए जा रहे हैं।", + "only": { + "none": "वे समूह जिनके लिए कुछ नहीं मिला", + "some": "वे समूह जिनके पास कोई संसाधन है", + "hnr": "वे समूह जो सुसमाचार सुन सकते हैं, पर जाँचे गए स्रोतों में उनके पास लिखित पवित्रशास्त्र नहीं है", + "both": "वे समूह जिनके पास पवित्रशास्त्र और फ़िल्म दोनों हैं" + }, + "showAll": "सभी {count} दिखाएँ", + "seeLargest": "सबसे बड़े {count} देखें →" + }, + "gapList": { + "title": { + "groups": "सबसे बड़े समूह जिनके लिए कुछ नहीं मिला", + "noprayer": "कुछ नहीं मिला, और कोई प्रार्थना नहीं कर रहा", + "hnr": "सुसमाचार सुन सकते हैं, पढ़ने को कोई पाठ नहीं", + "other": "कमी कहाँ केंद्रित है" + }, + "largestTap": "ये सबसे बड़े {count} हैं; वहाँ जाने के लिए किसी एक पर टैप करें।", + "relatedLabel": "संबंधित:", + "relatedHasResources": "संबंधित: {language} के लिए संसाधन हैं" + }, + "province": { + "largestWithNothing": "कुछ भी न होने वाले सबसे बड़े: {examples}", + "wayIn": "पहुँच का रास्ता: {ways} — {basis}।", + "basisCountryLevel": "देश-स्तर पर" + }, + "related": { + "nothingInItself": "स्वयं {language} में कुछ नहीं" + }, + "deaf": { + "sunTaughtIn": "{organisation} पहले से ही {countries} में SUN सिखा रहा है।" } } diff --git a/library/i18n/locales/hi/messages.json b/library/i18n/locales/hi/messages.json index f5ac2888..f6a74d5a 100644 --- a/library/i18n/locales/hi/messages.json +++ b/library/i18n/locales/hi/messages.json @@ -21,5 +21,12 @@ "engineNotLoaded": "मानचित्र इंजन लोड नहीं हुआ", "engineNotLoadedDetail": "{engine} मानचित्र इंजन लोड नहीं हुआ। सूची और लेजेंड अप्रभावित हैं।", "viewLoadFailed": "यह दृश्य लोड नहीं हो सका।", - "retry": "फिर से कोशिश करें" + "retry": "फिर से कोशिश करें", + "noMapboxToken": "कोई Mapbox टोकन नहीं", + "noMapboxTokenDetail": "कोई Mapbox टोकन कॉन्फ़िगर नहीं है। सूची और लेजेंड पर कोई असर नहीं है।", + "loadingBoundaries": "सीमाएँ लोड हो रही हैं…", + "fetchingUupgs": "{n} UUPG लाए जा रहे हैं…", + "mapStatusCounts": "क्षेत्र: {polys} · ज़ोन: {zones}/{total} · पिन: {pins}", + "tapTerritoriesHint": "जोड़ने के लिए मानचित्र पर क्षेत्रों या लेजेंड की पंक्तियों पर टैप करें · हटाने के लिए फिर से टैप करें", + "territoriesSelected": "क्षेत्र: {count}" } diff --git a/library/i18n/locales/hi/poster.json b/library/i18n/locales/hi/poster.json index a49b2ec6..9f1471bf 100644 --- a/library/i18n/locales/hi/poster.json +++ b/library/i18n/locales/hi/poster.json @@ -18,5 +18,10 @@ "cancel": "रद्द करें", "map_preview": "मानचित्र पूर्वावलोकन", "loading_snapshot": "मानचित्र स्नैपशॉट लोड हो रहा है…", - "preview_unavailable": "पूर्वावलोकन उपलब्ध नहीं" + "preview_unavailable": "पूर्वावलोकन उपलब्ध नहीं", + "region_africa": "अफ़्रीका", + "region_middle_east": "मध्य पूर्व", + "region_central_asia": "मध्य एशिया", + "region_southeast_asia_oceania": "दक्षिण-पूर्व एशिया + ओशिनिया", + "region_china_mongolia": "चीन + मंगोलिया" } diff --git a/library/i18n/locales/it/gospel.json b/library/i18n/locales/it/gospel.json index d249c92e..6531997b 100644 --- a/library/i18n/locales/it/gospel.json +++ b/library/i18n/locales/it/gospel.json @@ -11,7 +11,8 @@ "placeholder": "Cerca un popolo, una lingua o un codice ISO", "clear": "Cancella ricerca", "none": "Nessuna corrispondenza per quel nome, lingua o codice.", - "placeholderShort": "Popolo, lingua o codice ISO" + "placeholderShort": "Popolo, lingua o codice ISO", + "showingOf": "Mostrati {shown} di {total} risultati: continua a digitare per restringere." }, "legend": { "title": "Popoli non raggiunti senza alcuna risorsa del vangelo", @@ -66,7 +67,8 @@ "haveAudio": "Hanno le Scritture in audio", "signLanguages": "Lingue dei segni", "growingUnreached": "In crescita e non raggiunti", - "deathsDay": "Decessi stimati/giorno" + "deathsDay": "Decessi stimati/giorno", + "alreadyPraying": "Già in preghiera: {n}" }, "sheet": { "ownLanguage": "Nella loro lingua", @@ -92,7 +94,17 @@ "audioScripture": "Scritture in audio", "bibleStoriesPlain": "Storie bibliche", "nParts": "{n} parti", - "zumeInLanguage": "Zume, in {language}" + "zumeInLanguage": "Zume, in {language}", + "jesusFilmTitle": "Apre il film JESUS in questa lingua su jesusfilm.org.", + "sunSigned": "SUN (in lingua dei segni)", + "readOnline": "leggi online", + "studyNotes": "note di studio", + "seeSite": "vedi il sito", + "openPrayerPage": "Apri la pagina di preghiera per {name} su doxa.life.", + "everyResourceIn": "Tutte le risorse usate: {country}", + "macroNote": "ISO 639-3 classifica questo sotto la macrolingua {macro} insieme a {lang}, che ha risorse. È così che lo standard raggruppa i codici, non un’affermazione di mutua intelligibilità.", + "videoCovers": "Il video copre: {covers}. È un video biblico, non il film JESUS.", + "bibleVideoTitle": "{name} — video di narrazione biblica. NON è il film JESUS. Copre: {covers}" }, "state": { "noneFound": "nulla trovato", @@ -103,7 +115,10 @@ "unchecked": "non verificato", "linkDead": "link non attivo", "wrongLanguage": "lingua errata", - "varietyNotStated": "(varietà non indicata)" + "varietyNotStated": "(varietà non indicata)", + "loading": "Caricamento", + "linkNotChecked": "Questo link non è stato verificato.", + "noCard": "Nessuna scheda per \"{slug}\"." }, "guide": { "dotVsCountry": "Ogni punto è UN popolo di Doxa con la sua lingua; un paese è il NUMERO di quei popoli.", @@ -113,7 +128,8 @@ "isSignLanguage": "Questa è una lingua dei segni.", "wayInSome": "{n} di quei {total}", "wayInAll": "Ognuno di quei {n}", - "wayInWhat": "ha una via d’accesso: una lingua ponte, o SUN per un gruppo sordo. Tocca uno per vederla." + "wayInWhat": "ha una via d’accesso: una lingua ponte, o SUN per un gruppo sordo. Tocca uno per vederla.", + "numberMadeOf": "Di che cosa è fatto quel numero." }, "tables": { "title": "Risorse del Vangelo — Dati", @@ -135,7 +151,9 @@ "t_resources": "Risorse per popolo", "t_languages": "Lingue per paese", "t_growth": "Accesso vs crescita", - "t_bridgereach": "Una lingua, quanti popoli" + "t_bridgereach": "Una lingua, quanti popoli", + "growingHeroLead": "{count} popoli sono in crescita", + "growingHeroTail": "{population} persone, con una stima di {deaths} morti al giorno." }, "pages": { "listTitle": "Risorse del Vangelo per lingua", @@ -317,5 +335,38 @@ "c3Title": "Paesi con più lingue non servite", "c3Sub": "I {n} paesi con più lingue per cui non è stato trovato nulla. La lunghezza della barra è ogni lingua presente in quel paese.", "c3Unit": "lingue presenti nel paese (una lingua transfrontaliera conta in ciascuno)" + }, + "filter": { + "showingOnly": "Sono mostrati solo {what}.", + "only": { + "none": "gruppi per cui non è stato trovato nulla", + "some": "gruppi con almeno una risorsa", + "hnr": "gruppi che possono ascoltare il Vangelo ma non hanno Scrittura scritta nelle fonti controllate", + "both": "gruppi con Scrittura e film" + }, + "showAll": "Mostra tutti i {count}", + "seeLargest": "Vedi i {count} più grandi →" + }, + "gapList": { + "title": { + "groups": "Gruppi più grandi per cui non è stato trovato nulla", + "noprayer": "Nulla trovato, e nessuno che prega", + "hnr": "Possono ascoltare il Vangelo, nessun testo da leggere", + "other": "Dove si concentra la lacuna" + }, + "largestTap": "Questi sono i {count} più grandi; toccane uno per andarci.", + "relatedLabel": "correlato:", + "relatedHasResources": "correlato: {language} ha risorse" + }, + "province": { + "largestWithNothing": "I più grandi senza nulla: {examples}", + "wayIn": "Via d’accesso: {ways} — {basis}.", + "basisCountryLevel": "a livello nazionale" + }, + "related": { + "nothingInItself": "Niente in {language} in sé" + }, + "deaf": { + "sunTaughtIn": "SUN è già insegnato da {organisation} in: {countries}." } } diff --git a/library/i18n/locales/it/messages.json b/library/i18n/locales/it/messages.json index 81adcd0e..2de7270a 100644 --- a/library/i18n/locales/it/messages.json +++ b/library/i18n/locales/it/messages.json @@ -21,5 +21,12 @@ "engineNotLoaded": "motore di mappe non caricato", "engineNotLoadedDetail": "Il motore di mappe {engine} non si è caricato. L’elenco e la legenda non sono interessati.", "viewLoadFailed": "Impossibile caricare questa vista.", - "retry": "Riprova" + "retry": "Riprova", + "noMapboxToken": "Nessun token Mapbox", + "noMapboxTokenDetail": "Nessun token Mapbox configurato. L’elenco e la legenda non sono interessati.", + "loadingBoundaries": "Caricamento dei confini…", + "fetchingUupgs": "Recupero di {n} UUPG…", + "mapStatusCounts": "Aree: {polys} · zone: {zones}/{total} · segnaposto: {pins}", + "tapTerritoriesHint": "Tocca i territori sulla mappa o le righe della legenda per aggiungerli · tocca di nuovo per rimuoverli", + "territoriesSelected": "Territori: {count}" } diff --git a/library/i18n/locales/it/poster.json b/library/i18n/locales/it/poster.json index ce027a2e..0c5e8a3f 100644 --- a/library/i18n/locales/it/poster.json +++ b/library/i18n/locales/it/poster.json @@ -18,5 +18,10 @@ "cancel": "Annulla", "map_preview": "anteprima della mappa", "loading_snapshot": "Caricamento dell'istantanea della mappa…", - "preview_unavailable": "anteprima non disponibile" + "preview_unavailable": "anteprima non disponibile", + "region_africa": "Africa", + "region_middle_east": "Medio Oriente", + "region_central_asia": "Asia centrale", + "region_southeast_asia_oceania": "Sud-est asiatico + Oceania", + "region_china_mongolia": "Cina + Mongolia" } diff --git a/library/i18n/locales/pt/gospel.json b/library/i18n/locales/pt/gospel.json index 35a222c5..46cab7f1 100644 --- a/library/i18n/locales/pt/gospel.json +++ b/library/i18n/locales/pt/gospel.json @@ -11,7 +11,8 @@ "placeholder": "Procure um povo, idioma ou código ISO", "clear": "Limpar busca", "none": "Nada corresponde a esse nome, idioma ou código.", - "placeholderShort": "Povo, idioma ou código ISO" + "placeholderShort": "Povo, idioma ou código ISO", + "showingOf": "Mostrando {shown} de {total} resultados — continue digitando para refinar." }, "legend": { "title": "Povos não alcançados sem nenhum recurso do evangelho", @@ -66,7 +67,8 @@ "haveAudio": "Têm Escrituras em áudio", "signLanguages": "Línguas de sinais", "growingUnreached": "Em crescimento e não alcançados", - "deathsDay": "Mortes estimadas/dia" + "deathsDay": "Mortes estimadas/dia", + "alreadyPraying": "Já orando: {n}" }, "sheet": { "ownLanguage": "No próprio idioma", @@ -92,7 +94,17 @@ "audioScripture": "Escrituras em áudio", "bibleStoriesPlain": "Histórias bíblicas", "nParts": "{n} partes", - "zumeInLanguage": "Zume, em {language}" + "zumeInLanguage": "Zume, em {language}", + "jesusFilmTitle": "Abre o filme JESUS neste idioma em jesusfilm.org.", + "sunSigned": "SUN (em língua de sinais)", + "readOnline": "ler online", + "studyNotes": "notas de estudo", + "seeSite": "ver o site", + "openPrayerPage": "Abrir a página de oração de {name} em doxa.life.", + "everyResourceIn": "Todos os recursos usados: {country}", + "macroNote": "A ISO 639-3 classifica isto sob a macrolíngua {macro} com {lang}, que tem recursos. É assim que a norma agrupa os códigos, não uma afirmação de inteligibilidade mútua.", + "videoCovers": "O vídeo abrange: {covers}. É vídeo bíblico, não o filme JESUS.", + "bibleVideoTitle": "{name} — vídeo de narrativa bíblica. NÃO é o filme JESUS. Abrange: {covers}" }, "state": { "noneFound": "nada encontrado", @@ -103,7 +115,10 @@ "unchecked": "não verificado", "linkDead": "link inativo", "wrongLanguage": "idioma incorreto", - "varietyNotStated": "(variedade não indicada)" + "varietyNotStated": "(variedade não indicada)", + "loading": "Carregando", + "linkNotChecked": "Este link não foi verificado.", + "noCard": "Nenhum cartão para \"{slug}\"." }, "guide": { "dotVsCountry": "Cada ponto é UM povo da Doxa e o seu próprio idioma; um país é o NÚMERO desses povos.", @@ -113,7 +128,8 @@ "isSignLanguage": "Esta é uma língua de sinais.", "wayInSome": "{n} desses {total}", "wayInAll": "Cada um desses {n}", - "wayInWhat": "tem uma porta de entrada — uma língua-ponte, ou SUN para um grupo surdo. Toque num para ver." + "wayInWhat": "tem uma porta de entrada — uma língua-ponte, ou SUN para um grupo surdo. Toque num para ver.", + "numberMadeOf": "Do que esse número é feito." }, "tables": { "title": "Recursos do evangelho — Dados", @@ -135,7 +151,9 @@ "t_resources": "Recursos por povo", "t_languages": "Idiomas por país", "t_growth": "Acesso vs crescimento", - "t_bridgereach": "Uma língua, quantos povos" + "t_bridgereach": "Uma língua, quantos povos", + "growingHeroLead": "{count} povos estão crescendo", + "growingHeroTail": "{population} pessoas, com cerca de {deaths} mortes por dia." }, "pages": { "listTitle": "Recursos do evangelho por idioma", @@ -317,5 +335,38 @@ "c3Title": "Países com mais línguas desatendidas", "c3Sub": "Os {n} países com mais línguas sem nada encontrado. O comprimento da barra é cada língua presente nesse país.", "c3Unit": "línguas presentes no país (uma língua transfronteiriça conta em cada um)" + }, + "filter": { + "showingOnly": "Mostrando apenas {what}.", + "only": { + "none": "grupos sem nada encontrado", + "some": "grupos com algum recurso", + "hnr": "grupos que podem ouvir o evangelho, mas não têm Escritura escrita nas fontes verificadas", + "both": "grupos com Escritura e filme" + }, + "showAll": "Mostrar todos os {count}", + "seeLargest": "Ver os {count} maiores →" + }, + "gapList": { + "title": { + "groups": "Maiores grupos sem nada encontrado", + "noprayer": "Nada encontrado e ninguém orando", + "hnr": "Podem ouvir o evangelho, sem texto para ler", + "other": "Onde a lacuna se concentra" + }, + "largestTap": "Estes são os {count} maiores; toque em um para ir até lá.", + "relatedLabel": "relacionado:", + "relatedHasResources": "relacionado: {language} tem recursos" + }, + "province": { + "largestWithNothing": "Maiores sem nada: {examples}", + "wayIn": "Porta de entrada: {ways} — {basis}.", + "basisCountryLevel": "em nível de país" + }, + "related": { + "nothingInItself": "Nada em {language} em si" + }, + "deaf": { + "sunTaughtIn": "SUN já é ensinado por {organisation} em: {countries}." } } diff --git a/library/i18n/locales/pt/messages.json b/library/i18n/locales/pt/messages.json index d70ffc96..96b3ced5 100644 --- a/library/i18n/locales/pt/messages.json +++ b/library/i18n/locales/pt/messages.json @@ -21,5 +21,12 @@ "engineNotLoaded": "motor de mapas não carregado", "engineNotLoadedDetail": "O motor de mapas {engine} não carregou. A lista e a legenda não são afetadas.", "viewLoadFailed": "Não foi possível carregar esta visualização.", - "retry": "Tentar novamente" + "retry": "Tentar novamente", + "noMapboxToken": "Nenhum token do Mapbox", + "noMapboxTokenDetail": "Nenhum token do Mapbox configurado. A lista e a legenda não são afetadas.", + "loadingBoundaries": "Carregando limites…", + "fetchingUupgs": "Buscando {n} UUPGs…", + "mapStatusCounts": "Áreas: {polys} · zonas: {zones}/{total} · marcadores: {pins}", + "tapTerritoriesHint": "Toque em territórios no mapa ou em linhas da legenda para adicionar · toque de novo para remover", + "territoriesSelected": "Territórios: {count}" } diff --git a/library/i18n/locales/pt/poster.json b/library/i18n/locales/pt/poster.json index 39f70564..2403fb05 100644 --- a/library/i18n/locales/pt/poster.json +++ b/library/i18n/locales/pt/poster.json @@ -18,5 +18,10 @@ "cancel": "Cancelar", "map_preview": "pré-visualização do mapa", "loading_snapshot": "Carregando o instantâneo do mapa…", - "preview_unavailable": "pré-visualização indisponível" + "preview_unavailable": "pré-visualização indisponível", + "region_africa": "África", + "region_middle_east": "Oriente Médio", + "region_central_asia": "Ásia Central", + "region_southeast_asia_oceania": "Sudeste Asiático + Oceania", + "region_china_mongolia": "China + Mongólia" } diff --git a/library/i18n/locales/ro/gospel.json b/library/i18n/locales/ro/gospel.json index 4c683a4c..33e88317 100644 --- a/library/i18n/locales/ro/gospel.json +++ b/library/i18n/locales/ro/gospel.json @@ -11,7 +11,8 @@ "placeholder": "Caută un popor, o limbă sau un cod ISO", "clear": "Șterge căutarea", "none": "Nimic nu se potrivește cu acel nume, limbă sau cod.", - "placeholderShort": "Popor, limbă sau cod ISO" + "placeholderShort": "Popor, limbă sau cod ISO", + "showingOf": "Se afișează {shown} din {total} rezultate — continuați să tastați pentru a restrânge." }, "legend": { "title": "Popoare neatinse, fără nicio resursă a Evangheliei", @@ -66,7 +67,8 @@ "haveAudio": "Au Scripturi audio", "signLanguages": "Limbaje semne", "growingUnreached": "În creștere și neatinse", - "deathsDay": "Decese estimate/zi" + "deathsDay": "Decese estimate/zi", + "alreadyPraying": "Se roagă deja: {n}" }, "sheet": { "ownLanguage": "În limba lor", @@ -92,7 +94,17 @@ "audioScripture": "Scripturi audio", "bibleStoriesPlain": "Povestiri biblice", "nParts": "{n} părți", - "zumeInLanguage": "Zume, în {language}" + "zumeInLanguage": "Zume, în {language}", + "jesusFilmTitle": "Deschide filmul ISUS în această limbă pe jesusfilm.org.", + "sunSigned": "SUN (în limbajul semnelor)", + "readOnline": "citește online", + "studyNotes": "note de studiu", + "seeSite": "vezi site-ul", + "openPrayerPage": "Deschide pagina de rugăciune pentru {name} pe doxa.life.", + "everyResourceIn": "Toate resursele folosite în {country}", + "macroNote": "ISO 639-3 încadrează aceasta la macrolimba {macro}, împreună cu {lang}, care are resurse. Așa grupează standardul codurile; nu înseamnă că sunt reciproc inteligibile.", + "videoCovers": "Videoclipul acoperă: {covers}. Este video biblic, nu filmul ISUS.", + "bibleVideoTitle": "{name} — video cu narațiune biblică. NU este filmul ISUS. Acoperă: {covers}" }, "state": { "noneFound": "nimic găsit", @@ -103,7 +115,10 @@ "unchecked": "neverificat", "linkDead": "link mort", "wrongLanguage": "limbă greșită", - "varietyNotStated": "(varietate nespecificată)" + "varietyNotStated": "(varietate nespecificată)", + "loading": "Se încarcă", + "linkNotChecked": "Acest link nu a fost verificat.", + "noCard": "Nicio fișă pentru \"{slug}\"." }, "guide": { "dotVsCountry": "Fiecare punct este UN popor Doxa și limba lui; o țară este NUMĂRUL acelor popoare.", @@ -113,7 +128,8 @@ "isSignLanguage": "Aceasta este o limbă a semnelor.", "wayInSome": "{n} din cele {total}", "wayInAll": "Fiecare dintre cele {n}", - "wayInWhat": "are o cale de acces — o limbă-punte sau SUN pentru un grup de surzi. Atinge unul pentru a o vedea." + "wayInWhat": "are o cale de acces — o limbă-punte sau SUN pentru un grup de surzi. Atinge unul pentru a o vedea.", + "numberMadeOf": "Din ce este alcătuit acest număr." }, "tables": { "title": "Resurse ale Evangheliei — Date", @@ -135,7 +151,9 @@ "t_resources": "Resurse pe popoare", "t_languages": "Limbi pe țări", "t_growth": "Acces vs creștere", - "t_bridgereach": "O limbă, câte popoare" + "t_bridgereach": "O limbă, câte popoare", + "growingHeroLead": "{count} popoare sunt în creștere", + "growingHeroTail": "{population} de persoane, cu aproximativ {deaths} decese pe zi." }, "pages": { "listTitle": "Resurse ale Evangheliei pe limbi", @@ -317,5 +335,38 @@ "c3Title": "Țări cu cele mai multe limbi neservite", "c3Sub": "Cele {n} țări cu cele mai multe limbi pentru care nu s-a găsit nimic. Lungimea barei este fiecare limbă prezentă în acea țară.", "c3Unit": "limbi prezente în țară (o limbă transfrontalieră se numără în fiecare)" + }, + "filter": { + "showingOnly": "Se afișează doar {what}.", + "only": { + "none": "grupurile pentru care nu s-a găsit nimic", + "some": "grupurile cu cel puțin o resursă", + "hnr": "grupurile care pot auzi Evanghelia, dar nu au Scriptură scrisă în sursele verificate", + "both": "grupurile cu Scriptură și film" + }, + "showAll": "Afișează toate cele {count}", + "seeLargest": "Vezi cele mai mari {count} →" + }, + "gapList": { + "title": { + "groups": "Cele mai mari grupuri pentru care nu s-a găsit nimic", + "noprayer": "Nimic găsit și nimeni nu se roagă", + "hnr": "Pot auzi Evanghelia, fără text de citit", + "other": "Unde se concentrează golul" + }, + "largestTap": "Acestea sunt cele mai mari {count}; atingeți unul pentru a merge acolo.", + "relatedLabel": "înrudit:", + "relatedHasResources": "înrudit: {language} are resurse" + }, + "province": { + "largestWithNothing": "Cele mai mari fără nimic: {examples}", + "wayIn": "Cale de acces: {ways} — {basis}.", + "basisCountryLevel": "la nivel de țară" + }, + "related": { + "nothingInItself": "Nimic în {language} propriu-zisă" + }, + "deaf": { + "sunTaughtIn": "SUN este deja predat în {countries} de {organisation}." } } diff --git a/library/i18n/locales/ro/messages.json b/library/i18n/locales/ro/messages.json index 7a058c63..f5d5f00b 100644 --- a/library/i18n/locales/ro/messages.json +++ b/library/i18n/locales/ro/messages.json @@ -21,5 +21,12 @@ "engineNotLoaded": "motorul de hărți nu s-a încărcat", "engineNotLoadedDetail": "Motorul de hărți {engine} nu s-a încărcat. Lista și legenda nu sunt afectate.", "viewLoadFailed": "Această vizualizare nu s-a putut încărca.", - "retry": "Reîncearcă" + "retry": "Reîncearcă", + "noMapboxToken": "Niciun token Mapbox", + "noMapboxTokenDetail": "Nu este configurat niciun token Mapbox. Lista și legenda nu sunt afectate.", + "loadingBoundaries": "Se încarcă granițele…", + "fetchingUupgs": "Se preiau {n} UUPG…", + "mapStatusCounts": "Suprafețe: {polys} · zone: {zones}/{total} · repere: {pins}", + "tapTerritoriesHint": "Atingeți teritorii pe hartă sau rânduri din legendă pentru a le adăuga · atingeți din nou pentru a le elimina", + "territoriesSelected": "Teritorii: {count}" } diff --git a/library/i18n/locales/ro/poster.json b/library/i18n/locales/ro/poster.json index cf4b764a..4776be5d 100644 --- a/library/i18n/locales/ro/poster.json +++ b/library/i18n/locales/ro/poster.json @@ -18,5 +18,10 @@ "cancel": "Anulează", "map_preview": "previzualizare hartă", "loading_snapshot": "Se încarcă instantaneul hărții…", - "preview_unavailable": "previzualizare indisponibilă" + "preview_unavailable": "previzualizare indisponibilă", + "region_africa": "Africa", + "region_middle_east": "Orientul Mijlociu", + "region_central_asia": "Asia Centrală", + "region_southeast_asia_oceania": "Asia de Sud-Est + Oceania", + "region_china_mongolia": "China + Mongolia" } diff --git a/library/i18n/locales/ru/gospel.json b/library/i18n/locales/ru/gospel.json index a1556f55..df278994 100644 --- a/library/i18n/locales/ru/gospel.json +++ b/library/i18n/locales/ru/gospel.json @@ -11,7 +11,8 @@ "placeholder": "Найдите народ, язык или код ISO", "clear": "Очистить поиск", "none": "Ничего не найдено по этому названию, языку или коду.", - "placeholderShort": "Народ, язык или код ISO" + "placeholderShort": "Народ, язык или код ISO", + "showingOf": "Показано {shown} из {total} совпадений — продолжайте вводить, чтобы сузить поиск." }, "legend": { "title": "Недостигнутые народы, для которых не найдено ни одного ресурса", @@ -66,7 +67,8 @@ "haveAudio": "Есть аудио-Писание", "signLanguages": "Жестовые языки", "growingUnreached": "Растущие и недостигнутые", - "deathsDay": "Оценка смертей в день" + "deathsDay": "Оценка смертей в день", + "alreadyPraying": "Уже молятся: {n}" }, "sheet": { "ownLanguage": "На их родном языке", @@ -92,7 +94,17 @@ "audioScripture": "Аудио с Писанием", "bibleStoriesPlain": "Библейские истории", "nParts": "частей: {n}", - "zumeInLanguage": "Zume, на языке {language}" + "zumeInLanguage": "Zume, на языке {language}", + "jesusFilmTitle": "Открывает фильм «Иисус» на этом языке на jesusfilm.org.", + "sunSigned": "SUN (жестовый язык)", + "readOnline": "читать онлайн", + "studyNotes": "учебные примечания", + "seeSite": "см. сайт", + "openPrayerPage": "Открыть страницу молитвы за {name} на doxa.life.", + "everyResourceIn": "Все ресурсы, используемые в стране {country}", + "macroNote": "ISO 639-3 относит это к макроязыку {macro} вместе с {lang}, для которого есть ресурсы. Так стандарт группирует коды; это не утверждение о взаимопонимаемости.", + "videoCovers": "Видео охватывает: {covers}. Это библейское видео, а не фильм «Иисус».", + "bibleVideoTitle": "{name} — видео с библейскими повествованиями. Это НЕ фильм «Иисус». Охватывает: {covers}" }, "state": { "noneFound": "ничего не найдено", @@ -103,7 +115,10 @@ "unchecked": "не проверено", "linkDead": "ссылка не работает", "wrongLanguage": "не тот язык", - "varietyNotStated": "(вариант не указан)" + "varietyNotStated": "(вариант не указан)", + "loading": "Загрузка", + "linkNotChecked": "Эта ссылка не проверялась.", + "noCard": "Нет карточки для «{slug}»." }, "guide": { "dotVsCountry": "Точка — это ОДИН народ Doxa и его родной язык; цвет страны — это ЧИСЛО таких народов.", @@ -113,7 +128,8 @@ "isSignLanguage": "Это жестовый язык.", "wayInSome": "{n} из этих {total}", "wayInAll": "Каждая из этих {n}", - "wayInWhat": "имеет путь — язык-посредник или SUN для группы глухих. Нажмите на одну, чтобы увидеть его." + "wayInWhat": "имеет путь — язык-посредник или SUN для группы глухих. Нажмите на одну, чтобы увидеть его.", + "numberMadeOf": "Из чего складывается это число." }, "tables": { "title": "Ресурсы Евангелия — данные", @@ -135,7 +151,9 @@ "t_resources": "Ресурсы по народам", "t_languages": "Языки по странам", "t_growth": "Доступ и рост населения", - "t_bridgereach": "Один язык — сколько народов" + "t_bridgereach": "Один язык — сколько народов", + "growingHeroLead": "Растущих народов: {count}", + "growingHeroTail": "население: {population}; смертность — около {deaths} в день." }, "pages": { "listTitle": "Ресурсы Евангелия по языкам", @@ -317,5 +335,38 @@ "c3Title": "Страны с наибольшим числом неохваченных языков", "c3Sub": "{n} стран с наибольшим числом языков, для которых ничего не найдено. Длина столбца — каждый язык, присутствующий в этой стране.", "c3Unit": "языки, присутствующие в стране (трансграничный язык считается в каждой)" + }, + "filter": { + "showingOnly": "Показаны только {what}.", + "only": { + "none": "группы, для которых ничего не найдено", + "some": "группы, у которых есть хотя бы один ресурс", + "hnr": "группы, которые могут услышать Евангелие, но не имеют письменного Писания в проверенных источниках", + "both": "группы, у которых есть Писание и фильм" + }, + "showAll": "Показать все ({count})", + "seeLargest": "Самые крупные ({count}) →" + }, + "gapList": { + "title": { + "groups": "Крупнейшие группы, для которых ничего не найдено", + "noprayer": "Ничего не найдено, и никто не молится", + "hnr": "Могут услышать Евангелие, но нечего читать", + "other": "Где сосредоточен пробел" + }, + "largestTap": "Это {count} крупнейших; нажмите на одну, чтобы перейти к ней.", + "relatedLabel": "связанный:", + "relatedHasResources": "связанный: для {language} есть ресурсы" + }, + "province": { + "largestWithNothing": "Крупнейшие без ресурсов: {examples}", + "wayIn": "Путь к ним: {ways} — {basis}.", + "basisCountryLevel": "на уровне страны" + }, + "related": { + "nothingInItself": "Ничего на самом языке {language}" + }, + "deaf": { + "sunTaughtIn": "{organisation} уже преподаёт SUN: {countries}." } } diff --git a/library/i18n/locales/ru/messages.json b/library/i18n/locales/ru/messages.json index b82655e8..b6f261da 100644 --- a/library/i18n/locales/ru/messages.json +++ b/library/i18n/locales/ru/messages.json @@ -21,5 +21,12 @@ "engineNotLoaded": "движок карты не загружен", "engineNotLoadedDetail": "Движок карты {engine} не загрузился. Список и легенда не затронуты.", "viewLoadFailed": "Не удалось загрузить этот раздел.", - "retry": "Повторить" + "retry": "Повторить", + "noMapboxToken": "Нет токена Mapbox", + "noMapboxTokenDetail": "Токен Mapbox не настроен. Список и легенда не затронуты.", + "loadingBoundaries": "Загрузка границ…", + "fetchingUupgs": "Загрузка UUPG: {n}…", + "mapStatusCounts": "Области: {polys} · зоны: {zones}/{total} · метки: {pins}", + "tapTerritoriesHint": "Нажимайте на территории на карте или строки легенды, чтобы добавить · нажмите ещё раз, чтобы убрать", + "territoriesSelected": "Территории: {count}" } diff --git a/library/i18n/locales/ru/poster.json b/library/i18n/locales/ru/poster.json index 0bf80cf2..20965030 100644 --- a/library/i18n/locales/ru/poster.json +++ b/library/i18n/locales/ru/poster.json @@ -18,5 +18,10 @@ "cancel": "Отмена", "map_preview": "предпросмотр карты", "loading_snapshot": "Загрузка снимка карты…", - "preview_unavailable": "предпросмотр недоступен" + "preview_unavailable": "предпросмотр недоступен", + "region_africa": "Африка", + "region_middle_east": "Ближний Восток", + "region_central_asia": "Центральная Азия", + "region_southeast_asia_oceania": "Юго-Восточная Азия и Океания", + "region_china_mongolia": "Китай и Монголия" } diff --git a/library/i18n/locales/zh/gospel.json b/library/i18n/locales/zh/gospel.json index c990be3a..68a04bd4 100644 --- a/library/i18n/locales/zh/gospel.json +++ b/library/i18n/locales/zh/gospel.json @@ -11,7 +11,8 @@ "placeholder": "搜索族群、语言或 ISO 代码", "clear": "清除搜索", "none": "没有符合该名称、语言或代码的结果。", - "placeholderShort": "族群、语言或 ISO 代码" + "placeholderShort": "族群、语言或 ISO 代码", + "showingOf": "显示 {total} 个匹配中的 {shown} 个 — 继续输入以缩小范围。" }, "legend": { "title": "尚未接触且未找到任何福音资源的族群", @@ -66,7 +67,8 @@ "haveAudio": "有有声圣经", "signLanguages": "手语", "growingUnreached": "增长中且未接触", - "deathsDay": "每日估计死亡人数" + "deathsDay": "每日估计死亡人数", + "alreadyPraying": "已在代祷:{n}" }, "sheet": { "ownLanguage": "以他们自己的语言", @@ -92,7 +94,17 @@ "audioScripture": "音频圣经", "bibleStoriesPlain": "圣经故事", "nParts": "{n} 个部分", - "zumeInLanguage": "Zume — {language}" + "zumeInLanguage": "Zume — {language}", + "jesusFilmTitle": "在 jesusfilm.org 上打开此语言的《耶稣传》影片。", + "sunSigned": "SUN(手语)", + "readOnline": "在线阅读", + "studyNotes": "研读注释", + "seeSite": "见网站", + "openPrayerPage": "在 doxa.life 上打开为{name}祷告的页面。", + "everyResourceIn": "{country}使用的所有资源", + "macroNote": "ISO 639-3 将其与 {lang} 一起归入宏语言 {macro},而 {lang} 有资源。这只是该标准对代码的归类方式,并不表示两者可以互通。", + "videoCovers": "视频涵盖:{covers}。这是圣经视频,不是《耶稣传》影片。", + "bibleVideoTitle": "{name} — 圣经叙事视频。这不是《耶稣传》影片。涵盖:{covers}" }, "state": { "noneFound": "一无所获", @@ -103,7 +115,10 @@ "unchecked": "未核实", "linkDead": "链接失效", "wrongLanguage": "语言不符", - "varietyNotStated": "(未注明变体)" + "varietyNotStated": "(未注明变体)", + "loading": "加载中", + "linkNotChecked": "此链接尚未检查。", + "noCard": "没有\"{slug}\"的卡片。" }, "guide": { "dotVsCountry": "每个圆点代表 Doxa 的一个族群及其语言;国家的颜色代表这类族群的数量。", @@ -113,7 +128,8 @@ "isSignLanguage": "这是一种手语。", "wayInSome": "这 {total} 个中有 {n} 个", "wayInAll": "这 {n} 个中的每一个", - "wayInWhat": "都有切入点——一种桥梁语言,或聋人群体的 SUN。点按其中一个即可查看。" + "wayInWhat": "都有切入点——一种桥梁语言,或聋人群体的 SUN。点按其中一个即可查看。", + "numberMadeOf": "这个数字由什么构成。" }, "tables": { "title": "福音资源 — 数据", @@ -135,7 +151,9 @@ "t_resources": "按族群列出资源", "t_languages": "各国语言", "t_growth": "可及性与人口增长", - "t_bridgereach": "一种语言,覆盖多少民族" + "t_bridgereach": "一种语言,覆盖多少民族", + "growingHeroLead": "{count} 个民族正在增长", + "growingHeroTail": "{population} 人,估计每天有 {deaths} 人死亡。" }, "pages": { "listTitle": "按语言列出的福音资源", @@ -317,5 +335,38 @@ "c3Title": "未得服事语言最多的国家", "c3Sub": "一无所获语言最多的 {n} 个国家。条形长度为该国存在的全部语言。", "c3Unit": "该国存在的语言(跨境语言在每个国家各计一次)" + }, + "filter": { + "showingOnly": "仅显示{what}。", + "only": { + "none": "未找到任何资源的群体", + "some": "有某种资源的群体", + "hnr": "能听到福音、但在已查来源中没有文字圣经的群体", + "both": "有圣经和影片的群体" + }, + "showAll": "显示全部 {count} 个", + "seeLargest": "查看最大的 {count} 个 →" + }, + "gapList": { + "title": { + "groups": "未找到任何资源的最大群体", + "noprayer": "未找到任何资源,也无人代祷", + "hnr": "能听到福音,却没有可读的文字", + "other": "缺口集中在哪里" + }, + "largestTap": "这是最大的 {count} 个;点按其中一个即可飞到那里。", + "relatedLabel": "相关:", + "relatedHasResources": "相关:{language} 有资源" + }, + "province": { + "largestWithNothing": "毫无资源的最大群体:{examples}", + "wayIn": "切入途径:{ways} — {basis}。", + "basisCountryLevel": "国家层面" + }, + "related": { + "nothingInItself": "{language}本身没有任何资源" + }, + "deaf": { + "sunTaughtIn": "{organisation}已在{countries}教授 SUN。" } } diff --git a/library/i18n/locales/zh/messages.json b/library/i18n/locales/zh/messages.json index 4c7d0911..9fe40011 100644 --- a/library/i18n/locales/zh/messages.json +++ b/library/i18n/locales/zh/messages.json @@ -21,5 +21,12 @@ "engineNotLoaded": "地图引擎未加载", "engineNotLoadedDetail": "{engine} 地图引擎未能加载。列表和图例不受影响。", "viewLoadFailed": "此视图无法加载。", - "retry": "重试" + "retry": "重试", + "noMapboxToken": "没有 Mapbox 令牌", + "noMapboxTokenDetail": "未配置 Mapbox 令牌。列表和图例不受影响。", + "loadingBoundaries": "正在加载边界…", + "fetchingUupgs": "正在获取 {n} 个 UUPG…", + "mapStatusCounts": "区域:{polys} · 分区:{zones}/{total} · 标记:{pins}", + "tapTerritoriesHint": "点按地图上的区域或图例行即可添加 · 再次点按即可移除", + "territoriesSelected": "区域:{count}" } diff --git a/library/i18n/locales/zh/poster.json b/library/i18n/locales/zh/poster.json index d9e8b85f..5928e19e 100644 --- a/library/i18n/locales/zh/poster.json +++ b/library/i18n/locales/zh/poster.json @@ -18,5 +18,10 @@ "cancel": "取消", "map_preview": "地图预览", "loading_snapshot": "正在加载地图快照…", - "preview_unavailable": "预览不可用" + "preview_unavailable": "预览不可用", + "region_africa": "非洲", + "region_middle_east": "中东", + "region_central_asia": "中亚", + "region_southeast_asia_oceania": "东南亚 + 大洋洲", + "region_china_mongolia": "中国 + 蒙古" } diff --git a/plugin-designs/cities-internet/connectivity-map.vue b/plugin-designs/cities-internet/connectivity-map.vue index d92ac35d..d0a9062b 100644 --- a/plugin-designs/cities-internet/connectivity-map.vue +++ b/plugin-designs/cities-internet/connectivity-map.vue @@ -33,7 +33,8 @@ * carrier flag is a NAME-PATTERN HEURISTIC, not a verified classification. */ import { computed, inject, ref, shallowRef, markRaw, onMounted, onBeforeUnmount } from 'vue' -import { useOptionalT } from '@map/composables/useOptionalT.js' +import { useOptionalT, fillParams } from '@map/composables/useOptionalT.js' +import { tStatic } from '@map/i18n/index.js' import { useShadowStyles } from '@map/composables/useShadowStyles.js' import { addMapAttribution } from '@map/composables/useMapAttribution' @@ -374,12 +375,13 @@ onMounted(async () => { if (!gl) { const engine = ciProvider === 'maplibre' ? 'MapLibre' : 'Mapbox' console.warn(`[connectivity-map] the ${engine} engine did not load; refusing to substitute another engine.`) - // messages.engineMissing is the shared key (studio's landing 3); English only if a host has no catalogue. - const msg = t('messages.engineMissing', { engine }) - err.value = msg && msg !== 'messages.engineMissing' ? msg : `The ${engine} map engine did not load.` + // messages.engineMissing is the SHARED key, so read it qualified with tStatic: this file's t() prefixes + // every key with connectivity., which made it connectivity.messages.engineMissing, a miss, so the + // message was English in every locale. tStatic follows the page locale and falls back to English itself. + err.value = fillParams(tStatic('messages.engineMissing'), { engine }) return } - if (!mapEl.value) { err.value = 'map engine not loaded'; return } + if (!mapEl.value) { err.value = tStatic('messages.engineNotLoaded'); return } // RTL SHAPING, before the first map exists. This raw map does not go through useMapInstance // (which installs it), and the basemap draws each place in its OWN script: over the Middle East // most labels are Arabic, Persian or Hebrew for EVERY viewer. An entry-time call cannot do this, @@ -491,7 +493,7 @@ useShadowStyles( '.gm-sh-name{font-weight:800;font-size:15px}' + '.gm-ai{display:flex;align-items:flex-start;gap:8px;margin:0 0 10px;padding:7px 9px;border-radius:8px;font-size:11px;line-height:1.35;background:rgba(180,120,0,.12);color:#7a4f00;border:1px solid rgba(180,120,0,.25)}' + '.is-dark .gm-ai{background:rgba(255,200,80,.10);color:#f0cf88;border-color:rgba(255,200,80,.22)}' + - '.gm-ai-x{margin-left:auto;flex:none;border:0;background:none;cursor:pointer;font-size:15px;line-height:1;color:inherit;opacity:.7;padding:0 2px}' + + '.gm-ai-x{margin-left:auto;flex:none;border:0;background:none;cursor:pointer;font-size:15px;line-height:1;color:inherit;opacity:.7;padding:0 2px;min-width:24px;min-height:24px}' + // 24px: the AA target minimum; the glyph alone was ~15px '.gm-ai-x:hover{opacity:1}' + '.gm-x{margin-left:auto;border:1px solid rgba(0,0,0,.16);background:transparent;color:inherit;' + 'font:600 12px inherit;padding:6px 12px;border-radius:8px;cursor:pointer;min-height:34px}' + diff --git a/plugin-designs/cities-internet/data-tables.vue b/plugin-designs/cities-internet/data-tables.vue index 55fcee7e..8c5540c0 100644 --- a/plugin-designs/cities-internet/data-tables.vue +++ b/plugin-designs/cities-internet/data-tables.vue @@ -25,7 +25,8 @@ import { computed, inject, ref, watch } from 'vue' import DataTable from '@map/components/tables/DataTable.vue' import { useShadowStyles } from '@map/composables/useShadowStyles.js' import { resolveAssetsBase } from '@map/utils/embedBase.js' -import { useOptionalT } from '@map/composables/useOptionalT.js' +import { useOptionalT, fillParams } from '@map/composables/useOptionalT.js' +import { tStatic } from '@map/i18n/index.js' const profileConfig = inject('profileConfig', ref(null)) @@ -49,13 +50,13 @@ const t = useOptionalT('connectivity') const num = (n) => (Number(n) || 0).toLocaleString() const TABS = [ - { id: 'cities', label: 'Key Cities', file: 'key-cities' }, - { id: 'internet', label: 'Connectivity', file: 'country-connectivity' }, - { id: 'operators', label: 'ISPs & Carriers', file: 'network-operators' }, - { id: 'social', label: 'Social Platforms', file: 'social-platforms' }, + { id: 'cities', file: 'key-cities' }, + { id: 'internet', file: 'country-connectivity' }, + { id: 'operators', file: 'network-operators' }, + { id: 'social', file: 'social-platforms' }, // Messaging apps are the channel referral stats CANNOT see — WhatsApp tops 54 // countries, Telegram 38, and neither appears in StatCounter at all. - { id: 'messaging', label: 'Messaging Apps', file: 'messaging-channels' }, + { id: 'messaging', file: 'messaging-channels' }, ] const active = ref('cities') const current = computed(() => TABS.find((t) => t.id === active.value) || TABS[0]) @@ -73,9 +74,14 @@ async function ensure(file) { loading.value = true; loadErr.value = null try { const r = await fetch(`${assetsBase.value}/tables/${file}.json`) - if (!r.ok) throw new Error(`table not found (${r.status})`) + if (!r.ok) throw new Error(String(r.status)) cache.value = { ...cache.value, [file]: await r.json() } - } catch (e) { loadErr.value = String(e.message || e) } + } catch (_) { + // The reader sees the shared, translated "Could not load {name}." (qualified via tStatic: this file's + // t() would prefix connectivity.), naming the tab in their language; the status went to the console. + const tab = TABS.find((x) => x.file === file) + loadErr.value = fillParams(tStatic('messages.couldNotLoadNamed'), { name: tab ? t('tables.tabs.' + tab.id) : file }) + } finally { loading.value = false } } watch(current, (t) => t && ensure(t.file), { immediate: true }) diff --git a/plugin-designs/doxa-simple-map/doxa-simple-map.vue b/plugin-designs/doxa-simple-map/doxa-simple-map.vue index fdd9468e..b7169808 100644 --- a/plugin-designs/doxa-simple-map/doxa-simple-map.vue +++ b/plugin-designs/doxa-simple-map/doxa-simple-map.vue @@ -261,7 +261,7 @@ const isRTL = computed(() => RTL_LOCALES.has(locale.value)) // without overlapDensity. const FEATURES = { hamburgerMenu: false, - infoButton: false, + infoButton: false, // before enabling: the help modal needs role="dialog" + aria-modal + Escape + focus trap/return (A11Y-AUDIT-PRAYER-PROGRESS-CONNECTIVITY.md) overlapDensity: false, darkPinBorder: false, // Geocoder search bar feature-flagged OFF on simple-map per user request @@ -289,6 +289,7 @@ const tabs = computed(() => profileConfig?.value?.tabs ?? DEFAULT_TABS) // profileConfig.tabs is the host's own words and is shown as given. const DEFAULT_LABELS = Object.fromEntries(DEFAULT_TABS.map((d) => [d.id, d.label])) const tabLabel = (tab) => ((!tab.label || tab.label === DEFAULT_LABELS[tab.id]) && te('tabs.' + tab.id)) ? t('tabs.' + tab.id) : tab.label +// before giving an instance a 2nd tab: the tab bar needs role="tablist"/"tab" + :aria-selected (the underline already marks the active tab by shape, not only colour) const showTabBar = computed(() => tabs.value.length > 1) // ─── Tab state ──────────────────────────────────────────────────────────────── diff --git a/plugin-designs/my-upg-100-list/data/visual-gate.mjs b/plugin-designs/my-upg-100-list/data/visual-gate.mjs index 722bbaf9..667503eb 100644 --- a/plugin-designs/my-upg-100-list/data/visual-gate.mjs +++ b/plugin-designs/my-upg-100-list/data/visual-gate.mjs @@ -484,8 +484,9 @@ async function main() { // booted dark, so the first move used to be dark→light). This proves nothing // assumed the boot theme: handleToggleTheme flips isDark false→true → setStyle // dark-v11 → style.load re-adds tapestry/pins/isolation. The ThemeToggleButton - // is the .mcb control titled "Switch to …" — target it precisely. - const themeBtn = page.locator(`${EL} button.mcb[title^="Switch to"]`); + // is the .mcb control with data-control="theme" (studio L54) — untranslatable, + // unlike the "Switch to …" title this used to match, which is localized AND dynamic. + const themeBtn = page.locator(`${EL} button.mcb[data-control="theme"]`); let toggled = false; if (await themeBtn.count()) { await themeBtn.first().click().catch(() => {}); @@ -638,35 +639,25 @@ async function main() { await clearFilter(page); // ── (4) controls present in DOM (zoom, theme, fullscreen) ────────────────── - // ★ THESE TITLES ARE LOCALIZED (ZoomInButton.vue binds :title="t('buttons.zoomIn')", - // and MapControlButton mirrors title into aria-label). The page is pinned to en-US - // at newPage({ locale }) above (L4/LESSON-03), so matching the English string is not - // failing TODAY — but it is the same coupling that broke the (G-rail) probe when - // Batch Select became a bound title, so the expected strings are resolved from the - // SAME i18n source the component reads instead of being hardcoded here. If this gate - // is ever run in another locale, it keeps working. + // ★ QUERY data-control, NEVER THE TITLE (studio L54). Each wrapper writes + // data-control="zoom-in|zoom-out|fullscreen|theme|location|help|share" on + // MapControlButton; it is not a declared prop, so it falls through onto the + // rendered