i18n: 408 strings copied from DX-life, 8 locales, RTL, and the data follows too
Localisation was the lowest dimension on the board at 12. The repo already had
the vue-i18n layer — 11 locales, one JSON per area, eager glob, fallbackLocale
en — so this adds an AREA, not a mechanism: library/i18n/locales/<lc>/
regions.json, 51 keys x 8 locales, every string COPIED from their own
common.json rather than translated by me. The composer takes the namespace from
the FILENAME, so the files are flat like legend.json (a "regions" wrapper would
have produced regions.regions.*).
★ THE REASON IT DIDN'T WORK, AND WHY IT FAILED SILENTLY. With the Spanish
catalogue provably in the bundle, every string still rendered English. The
bundle installed i18n with app.use() but never app.provide('appI18n') — so
ProfileLoader could not drive the locale (it does exactly that at
ProfileLoader.vue:168-169, from profile-config.locale, its "established
contract"), and createAppI18n resolves its locale ONCE from <html lang>, which
every generated leaf page bakes as "en". One line, copied from
doxa-simple-map/index.js:84-86. It degraded quietly because the t() shim falls
back to its key, so a missing catalogue renders as English instead of throwing —
convenient in production, and exactly what hid this.
t() is a shim, not vue-i18n's t(), because their keys ARE English sentences and
several contain dots ('Loading results...', 'Type to search...') which is
vue-i18n's key separator. tm() returns the namespace object and we index it by
the literal string, so the port reads 1:1 with their source: t('Prayer
Coverage'). Plugin-local, per the placement rule — it is four lines shaped
entirely by how these pages were written.
THE DATA FOLLOWS THE LOCALE TOO, which the chrome alone would have hidden. The
prayer API localises its labels from ?lang=, and our components read
cfg.languageCode while the locale arrives as cfg.locale. Measured with ar: the
whole UI translated and every country in the table stayed English. Now the fetch
falls through locale/lang and refetches on change (Angola -> the Arabic list).
RTL: ar sets dir on OUR OWN root via RTL_LOCALES, the house pattern
(doxa-simple-map.vue:246/1346) — never document.documentElement, which belongs
to whatever page embeds us. Verified in a screenshot, not just an attribute: the
goal cards reverse order, the table right-aligns, the progress bars fill from
the right.
A CLONE FIX came free with it. Porting their uupgListTranslations.ts — the
authoritative list of what this component's labels are called — corrected
strings I had written from the markup instead: the rop1 filter is "People Group"
and I had "Affinity Bloc"; the search placeholder is the full "Search names,
country/continent, religions..."; prayer_coverage reads "Intercessors".
Two assertions of mine were wrong again, both failing on correct behaviour: the
back-link compared for equality when it carries their leading arrow, and the RTL
check read document.documentElement.dir when an embedded element must not set it.
Not done: it/ro/zh have no regions.json — DX-life translates 8 of our 11 — and
resolve through fallbackLocale en, which library/i18n/README says is fine.
Oswald (their Cyrillic heading face) is still unvendored.
English unchanged throughout: region 68/68 vs live, country 28/28, chooser, map
and dashboard suites all green, no gate newly red.
This commit is contained in:
parent
2a0976f834
commit
2b08a4e8de
18 changed files with 644 additions and 91 deletions
53
library/i18n/locales/ar/regions.json
Normal file
53
library/i18n/locales/ar/regions.json
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"%count% people groups in %countries% countries": "%count% مجموعة بشرية في %countries% دولة",
|
||||
"%region% has %count% people groups across %countries% countries. Each pin is coloured by how many people are praying for that people group.": "تضم %region% %count% مجموعة بشرية في %countries% دولة. يشير لون كل نقطة إلى عدد الأشخاص الذين يصلّون من أجل تلك المجموعة البشرية.",
|
||||
"1+ People Praying": "شخص واحد أو أكثر يصلّي",
|
||||
"100+ People Praying": "أكثر من 100 شخص يصلّون",
|
||||
"Adopted": "تم انتماؤه",
|
||||
"Adoption Progress": "تقدُّم الإنتماء",
|
||||
"All Regions": "جميع المناطق",
|
||||
"Alternate Names": "الأسماء البديلة",
|
||||
"Clear All": "مسح الكل",
|
||||
"Countries": "الدول",
|
||||
"Countries in this region": "الدول في هذه المنطقة",
|
||||
"Country": "الدولة",
|
||||
"Engaged": "مُنخرَط",
|
||||
"Engagement Progress": "تقدُّم الانخراط",
|
||||
"Every unengaged people group adopted by a church committed to prayer, giving, and sending": "كل مجموعة بشرية غير مبشرة تتبناها كنيسة ملتزمة بالصلاة والعطاء والإرسال",
|
||||
"Exact Search": "بحث دقيق",
|
||||
"Find unreached people groups by region and country.": "ابحث عن المجموعات البشرية غير المُبلَّغة حسب المنطقة والدولة.",
|
||||
"Full Profile": "الملف الشخصي الكامل",
|
||||
"Goal": "الهدف",
|
||||
"Hide Filters": "إخفاء المرشحات",
|
||||
"Intercessors": "المتشفعين",
|
||||
"Load More": "تحميل المزيد",
|
||||
"Loading results...": "النتائج قيد التحميل...",
|
||||
"Mobilize 100+ people praying daily for each DOXA people group": "حشد أكثر من 100 شخص للصلاة يوميًا من أجل كل مجموعة بشرية من مجموعات دوكسا",
|
||||
"No One Praying": "لا أحد يصلّي",
|
||||
"No options found": "لم يتم العثور على أي خيارات",
|
||||
"No unengaged people groups by 2033": "الهدف هو القضاء على جميع الشعوب الغير مخدومة روحيا بحلول عام 2033",
|
||||
"Not Adopted": "لم يتم انتماؤه",
|
||||
"Other": "أخرى",
|
||||
"Page Not Found": "الصفحة غير موجودة",
|
||||
"People Group": "مجموعة بشرية",
|
||||
"People Groups": "مجموعات بشرية",
|
||||
"People committed to praying": "الأشخاص الذين التزموا بالصلاة",
|
||||
"Population": "عدد السكان",
|
||||
"Prayer Coverage": "التغطية المستمرة بالصلاة",
|
||||
"Prayer coverage progress": "تقدم تغطية الصلاة",
|
||||
"Regions": "المناطق",
|
||||
"Religion": "الدين",
|
||||
"Search names, country/continent, religions...": "إبحث عن الأسماء، الدولة/القارة، الأديان...",
|
||||
"See %region% dashboard": "عرض لوحة معلومات %region%",
|
||||
"See All": "عرض جميع العناصر",
|
||||
"Select": "اختر",
|
||||
"Show Filters": "إظهار عوامل التصفية",
|
||||
"There are %count% people groups in %country%. Check the map for the people groups on the border of this country as well.": "توجد %count% مجموعات بشرية في %country%. تحقق من الخريطة لمعرفة المجموعات البشرية الواقعة على حدود هذه الدولة أيضًا.",
|
||||
"Total": "المجموع",
|
||||
"Type to search...": "اكتب ما تريد البحث عنه...",
|
||||
"Unengaged people groups": "الشعوب الغير متفاعلة",
|
||||
"WAGF Block": "كتلة WAGF",
|
||||
"WAGF Region": "منطقة WAGF",
|
||||
"people groups adopted": "الشعوب المنتمية",
|
||||
"people groups engaged": "مجموعات بشرية مُنخرِطة"
|
||||
}
|
||||
53
library/i18n/locales/de/regions.json
Normal file
53
library/i18n/locales/de/regions.json
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"%count% people groups in %countries% countries": "%count% Volksgruppen in %countries% Ländern",
|
||||
"%region% has %count% people groups across %countries% countries. Each pin is coloured by how many people are praying for that people group.": "%region% hat %count% Volksgruppen in %countries% Ländern. Die Farbe jedes Punktes zeigt, wie viele Menschen für diese Volksgruppe beten.",
|
||||
"1+ People Praying": "1+ Menschen beten",
|
||||
"100+ People Praying": "100+ Menschen beten",
|
||||
"Adopted": "Angenommen",
|
||||
"Adoption Progress": "Adoptionsfortschritt",
|
||||
"All Regions": "Alle Regionen",
|
||||
"Alternate Names": "Alternative Namen",
|
||||
"Clear All": "Alle löschen",
|
||||
"Countries": "Länder",
|
||||
"Countries in this region": "Länder in dieser Region",
|
||||
"Country": "Land",
|
||||
"Engaged": "Engagiert",
|
||||
"Engagement Progress": "Engagement-Fortschritt",
|
||||
"Every unengaged people group adopted by a church committed to prayer, giving, and sending": "Jede einzelne der völlig unerreichten Volksgruppen soll von einer Ortsgemeinde adoptiert werden, durch Beten, Geben und Senden",
|
||||
"Exact Search": "Exakte Suche",
|
||||
"Find unreached people groups by region and country.": "Finde unerreichte Volksgruppen nach Region und Land.",
|
||||
"Full Profile": "Vollständiges Profil",
|
||||
"Goal": "Ziel",
|
||||
"Hide Filters": "Filter ausblenden",
|
||||
"Intercessors": "Fürbitter",
|
||||
"Load More": "Mehr laden",
|
||||
"Loading results...": "Ergebnisse werden geladen...",
|
||||
"Mobilize 100+ people praying daily for each DOXA people group": "Mobilisiere 100+ Menschen, die täglich für jede DOXA-Volksgruppe beten",
|
||||
"No One Praying": "Niemand betet",
|
||||
"No options found": "Keine Standorte gefunden",
|
||||
"No unengaged people groups by 2033": "Dass es bis 2033 keine völlig unerreichte Volksgruppe mehr gibt",
|
||||
"Not Adopted": "noch nicht adoptiert",
|
||||
"Other": "Andere",
|
||||
"Page Not Found": "Seite nicht gefunden",
|
||||
"People Group": "Personengruppe",
|
||||
"People Groups": "Volksgruppen",
|
||||
"People committed to praying": "Menschen, die sich dem Beten verpflichtet haben",
|
||||
"Population": "Bevölkerung",
|
||||
"Prayer Coverage": "Gebetsabdeckung",
|
||||
"Prayer coverage progress": "Fortschritt der Gebetsabdeckung",
|
||||
"Regions": "Regionen",
|
||||
"Religion": "Primäre Religion",
|
||||
"Search names, country/continent, religions...": "Suche nach Namen, Land/Kontinent, Religionen...",
|
||||
"See %region% dashboard": "Dashboard anzeigen: %region%",
|
||||
"See All": "Alle anzeigen",
|
||||
"Select": "Auswählen",
|
||||
"Show Filters": "Filter anzeigen",
|
||||
"There are %count% people groups in %country%. Check the map for the people groups on the border of this country as well.": "In %country% gibt es %count% Volksgruppen. Sieh auch auf der Karte nach den Volksgruppen an der Grenze dieses Landes.",
|
||||
"Total": "Gesamt",
|
||||
"Type to search...": "Geben Sie etwas ein, um danach zu suchen",
|
||||
"Unengaged people groups": "Völlig unerreichte Volksgruppen",
|
||||
"WAGF Block": "WAGF-Block",
|
||||
"WAGF Region": "WAGF-Region",
|
||||
"people groups adopted": "adoptierte Volksgruppen",
|
||||
"people groups engaged": "engagierte Volksgruppen"
|
||||
}
|
||||
53
library/i18n/locales/en/regions.json
Normal file
53
library/i18n/locales/en/regions.json
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"%count% people groups in %countries% countries": "%count% people groups in %countries% countries",
|
||||
"%region% has %count% people groups across %countries% countries. Each pin is coloured by how many people are praying for that people group.": "%region% has %count% people groups across %countries% countries. Each pin is coloured by how many people are praying for that people group.",
|
||||
"1+ People Praying": "1+ People Praying",
|
||||
"100+ People Praying": "100+ People Praying",
|
||||
"Adopted": "Adopted",
|
||||
"Adoption Progress": "Adoption Progress",
|
||||
"All Regions": "All Regions",
|
||||
"Alternate Names": "Alternate Names",
|
||||
"Clear All": "Clear All",
|
||||
"Countries": "Countries",
|
||||
"Countries in this region": "Countries in this region",
|
||||
"Country": "Country",
|
||||
"Engaged": "Engaged",
|
||||
"Engagement Progress": "Engagement Progress",
|
||||
"Every unengaged people group adopted by a church committed to prayer, giving, and sending": "Every unengaged people group adopted by a church committed to prayer, giving, and sending",
|
||||
"Exact Search": "Exact Search",
|
||||
"Find unreached people groups by region and country.": "Find unreached people groups by region and country.",
|
||||
"Full Profile": "Full Profile",
|
||||
"Goal": "Goal",
|
||||
"Hide Filters": "Hide Filters",
|
||||
"Intercessors": "Intercessors",
|
||||
"Load More": "Load More",
|
||||
"Loading results...": "Loading results...",
|
||||
"Mobilize 100+ people praying daily for each DOXA people group": "Mobilize 100+ people praying daily for each DOXA people group",
|
||||
"No One Praying": "No One Praying",
|
||||
"No options found": "No options found",
|
||||
"No unengaged people groups by 2033": "No unengaged people groups by 2033",
|
||||
"Not Adopted": "Not Adopted",
|
||||
"Other": "Other",
|
||||
"Page Not Found": "Page Not Found",
|
||||
"People Group": "People Group",
|
||||
"People Groups": "People Groups",
|
||||
"People committed to praying": "People committed to praying",
|
||||
"Population": "Population",
|
||||
"Prayer Coverage": "Prayer Coverage",
|
||||
"Prayer coverage progress": "Prayer coverage progress",
|
||||
"Regions": "Regions",
|
||||
"Religion": "Religion",
|
||||
"Search names, country/continent, religions...": "Search names, country/continent, religions...",
|
||||
"See %region% dashboard": "See %region% dashboard",
|
||||
"See All": "See All",
|
||||
"Select": "Select",
|
||||
"Show Filters": "Show Filters",
|
||||
"There are %count% people groups in %country%. Check the map for the people groups on the border of this country as well.": "There are %count% people groups in %country%. Check the map for the people groups on the border of this country as well.",
|
||||
"Total": "Total",
|
||||
"Type to search...": "Type to search...",
|
||||
"Unengaged people groups": "Unengaged people groups",
|
||||
"WAGF Block": "WAGF Block",
|
||||
"WAGF Region": "WAGF Region",
|
||||
"people groups adopted": "people groups adopted",
|
||||
"people groups engaged": "people groups engaged"
|
||||
}
|
||||
53
library/i18n/locales/es/regions.json
Normal file
53
library/i18n/locales/es/regions.json
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"%count% people groups in %countries% countries": "%count% grupos étnicos en %countries% países",
|
||||
"%region% has %count% people groups across %countries% countries. Each pin is coloured by how many people are praying for that people group.": "%region% tiene %count% grupos étnicos en %countries% países. El color de cada punto indica cuántas personas oran por ese grupo étnico.",
|
||||
"1+ People Praying": "1+ personas orando",
|
||||
"100+ People Praying": "100+ personas orando",
|
||||
"Adopted": "Aprobado",
|
||||
"Adoption Progress": "Progreso de la adopción",
|
||||
"All Regions": "Todas las regiones",
|
||||
"Alternate Names": "Nombres alternativos",
|
||||
"Clear All": "Borrar todo",
|
||||
"Countries": "Países",
|
||||
"Countries in this region": "Países de esta región",
|
||||
"Country": "País",
|
||||
"Engaged": "Comprometido",
|
||||
"Engagement Progress": "Progreso del compromiso",
|
||||
"Every unengaged people group adopted by a church committed to prayer, giving, and sending": "Cada grupo étnico no evangelizado adoptado por una iglesia comprometida con la oración, la ofrenda y el envío.",
|
||||
"Exact Search": "Búsqueda exacta",
|
||||
"Find unreached people groups by region and country.": "Encuentra grupos étnicos no alcanzados por región y país.",
|
||||
"Full Profile": "Perfil completo",
|
||||
"Goal": "Meta",
|
||||
"Hide Filters": "Ocultar filtros",
|
||||
"Intercessors": "Intercesores",
|
||||
"Load More": "Cargar más",
|
||||
"Loading results...": "Cargando resultados...",
|
||||
"Mobilize 100+ people praying daily for each DOXA people group": "Movilizar a más de 100 personas que oren diariamente por cada grupo étnico DOXA.",
|
||||
"No One Praying": "Nadie orando",
|
||||
"No options found": "No se han encontrado opciones",
|
||||
"No unengaged people groups by 2033": "No habrá grupos étnicos sin evangelizar para 2033",
|
||||
"Not Adopted": "No adoptado",
|
||||
"Other": "Otros",
|
||||
"Page Not Found": "Página no encontrada",
|
||||
"People Group": "Grupo Étnico",
|
||||
"People Groups": "Grupos Étnicos",
|
||||
"People committed to praying": "Personas comprometidas con la oración",
|
||||
"Population": "Población",
|
||||
"Prayer Coverage": "Cobertura de oración",
|
||||
"Prayer coverage progress": "Progreso de la cobertura de oración",
|
||||
"Regions": "Regiones",
|
||||
"Religion": "Religión",
|
||||
"Search names, country/continent, religions...": "Buscar nombres, país/continente, religiones...",
|
||||
"See %region% dashboard": "Ver el panel de %region%",
|
||||
"See All": "Ver todo",
|
||||
"Select": "Seleccionar",
|
||||
"Show Filters": "Mostrar filtros",
|
||||
"There are %count% people groups in %country%. Check the map for the people groups on the border of this country as well.": "Hay %count% grupos étnicos en %country%. Consulta también el mapa para ver los grupos étnicos en la frontera de este país.",
|
||||
"Total": "Total",
|
||||
"Type to search...": "Escribe para buscar...",
|
||||
"Unengaged people groups": "Grupos étnicos no comprometidos",
|
||||
"WAGF Block": "Bloque WAGF",
|
||||
"WAGF Region": "Región de la WAGF",
|
||||
"people groups adopted": "grupos de personas adoptados",
|
||||
"people groups engaged": "grupos étnicos comprometidos"
|
||||
}
|
||||
53
library/i18n/locales/fr/regions.json
Normal file
53
library/i18n/locales/fr/regions.json
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"%count% people groups in %countries% countries": "%count% peuples dans %countries% pays",
|
||||
"%region% has %count% people groups across %countries% countries. Each pin is coloured by how many people are praying for that people group.": "%region% compte %count% peuples répartis dans %countries% pays. La couleur de chaque point indique combien de personnes prient pour ce peuple.",
|
||||
"1+ People Praying": "1+ personnes en prière",
|
||||
"100+ People Praying": "100+ personnes en prière",
|
||||
"Adopted": "Adopté",
|
||||
"Adoption Progress": "Progrès de l'adoption",
|
||||
"All Regions": "Toutes les régions",
|
||||
"Alternate Names": "Autres noms",
|
||||
"Clear All": "Tout effacer",
|
||||
"Countries": "Pays",
|
||||
"Countries in this region": "Pays de cette région",
|
||||
"Country": "Pays",
|
||||
"Engaged": "Engagé",
|
||||
"Engagement Progress": "Progrès de l'engagement",
|
||||
"Every unengaged people group adopted by a church committed to prayer, giving, and sending": "Chaque peuple non engagé adopté par une église locale engagée dans la prière, le don et l'envoi",
|
||||
"Exact Search": "Recherche exacte",
|
||||
"Find unreached people groups by region and country.": "Trouvez des peuples non atteints par région et par pays.",
|
||||
"Full Profile": "Profil complet",
|
||||
"Goal": "Objectif",
|
||||
"Hide Filters": "Masquer les filtres",
|
||||
"Intercessors": "Intercesseurs",
|
||||
"Load More": "Afficher plus",
|
||||
"Loading results...": "Chargement des résultats...",
|
||||
"Mobilize 100+ people praying daily for each DOXA people group": "Mobiliser plus de 100 personnes priant chaque jour pour chaque peuple DOXA",
|
||||
"No One Praying": "Personne ne prie",
|
||||
"No options found": "Aucun lieu trouvé",
|
||||
"No unengaged people groups by 2033": "Plus aucun peuple non engagé d'ici 2033",
|
||||
"Not Adopted": "Non adopté",
|
||||
"Other": "Autre",
|
||||
"Page Not Found": "Page introuvable",
|
||||
"People Group": "Peuple",
|
||||
"People Groups": "Peuples",
|
||||
"People committed to praying": "Personnes engagées dans la prière",
|
||||
"Population": "Population",
|
||||
"Prayer Coverage": "Couverture de prière",
|
||||
"Prayer coverage progress": "Progression de la couverture de prière",
|
||||
"Regions": "Régions",
|
||||
"Religion": "Religion",
|
||||
"Search names, country/continent, religions...": "Rechercher des noms, pays/continent, religions...",
|
||||
"See %region% dashboard": "Voir le tableau de bord : %region%",
|
||||
"See All": "Tout afficher",
|
||||
"Select": "Sélectionner",
|
||||
"Show Filters": "Afficher les filtres",
|
||||
"There are %count% people groups in %country%. Check the map for the people groups on the border of this country as well.": "Il y a %count% peuples en %country%. Consultez la carte pour découvrir également les peuples situés à la frontière de ce pays.",
|
||||
"Total": "Total",
|
||||
"Type to search...": "Entrer votre recherche",
|
||||
"Unengaged people groups": "Peuples non engagés",
|
||||
"WAGF Block": "Bloc WAGF",
|
||||
"WAGF Region": "Région WAGF",
|
||||
"people groups adopted": "peuples adoptés",
|
||||
"people groups engaged": "peuples engagés"
|
||||
}
|
||||
53
library/i18n/locales/hi/regions.json
Normal file
53
library/i18n/locales/hi/regions.json
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"%count% people groups in %countries% countries": "%countries% देशों में %count% लोगों के समूह",
|
||||
"%region% has %count% people groups across %countries% countries. Each pin is coloured by how many people are praying for that people group.": "%region% में %countries% देशों में %count% लोगों के समूह हैं। प्रत्येक बिंदु का रंग दर्शाता है कि उस लोगों के समूह के लिए कितने लोग प्रार्थना कर रहे हैं।",
|
||||
"1+ People Praying": "1+ लोग प्रार्थना कर रहे हैं",
|
||||
"100+ People Praying": "100+ लोग प्रार्थना कर रहे हैं",
|
||||
"Adopted": "दत्तक लिया गया",
|
||||
"Adoption Progress": "दत्तक ग्रहण की प्रगति",
|
||||
"All Regions": "सभी क्षेत्र",
|
||||
"Alternate Names": "वैकल्पिक नाम",
|
||||
"Clear All": "सभी साफ़ करें",
|
||||
"Countries": "देश",
|
||||
"Countries in this region": "इस क्षेत्र के देश",
|
||||
"Country": "देश",
|
||||
"Engaged": "प्रवेशित",
|
||||
"Engagement Progress": "प्रवेश की प्रगति",
|
||||
"Every unengaged people group adopted by a church committed to prayer, giving, and sending": "प्रत्येक असंलग्न जनसमूह जिसे प्रार्थना, दान और भेजने के लिए प्रतिबद्ध एक चर्च ने अपनाया है।",
|
||||
"Exact Search": "सटीक खोज",
|
||||
"Find unreached people groups by region and country.": "क्षेत्र और देश के अनुसार अगम्य लोगों के समूह खोजें।",
|
||||
"Full Profile": "पूर्ण प्रोफ़ाइल",
|
||||
"Goal": "लक्ष्य",
|
||||
"Hide Filters": "फ़िल्टर छिपाएँ",
|
||||
"Intercessors": "मध्यस्थ",
|
||||
"Load More": "और लोड करें",
|
||||
"Loading results...": "परिणाम लोड हो रहे हैं..।",
|
||||
"Mobilize 100+ people praying daily for each DOXA people group": "प्रत्येक DOXA जनसमूह के लिए प्रतिदिन प्रार्थना करने हेतु 100+ लोगों को सक्रिय करें।",
|
||||
"No One Praying": "कोई प्रार्थना नहीं कर रहा",
|
||||
"No options found": "कोई विकल्प नहीं मिला",
|
||||
"No unengaged people groups by 2033": "2033 तक कोई भी संलग्न न किया गया जनसमूह नहीं रहेगा।",
|
||||
"Not Adopted": "दत्तक नहीं लिया गया",
|
||||
"Other": "अन्य",
|
||||
"Page Not Found": "पृष्ठ नहीं मिला",
|
||||
"People Group": "लोगों का समूह",
|
||||
"People Groups": "लोगों के समूह",
|
||||
"People committed to praying": "प्रार्थना के लिए प्रतिबद्ध लोग",
|
||||
"Population": "जनसँख्या",
|
||||
"Prayer Coverage": "प्रार्थना कवरेज",
|
||||
"Prayer coverage progress": "प्रार्थना कवरेज की प्रगति",
|
||||
"Regions": "क्षेत्र",
|
||||
"Religion": "धर्म",
|
||||
"Search names, country/continent, religions...": "नाम, देश/महाद्वीप, धर्म खोजें..।",
|
||||
"See %region% dashboard": "%region% डैशबोर्ड देखें",
|
||||
"See All": "सभी देखें",
|
||||
"Select": "चयन/छंटनी",
|
||||
"Show Filters": "फ़िल्टर दिखाएँ",
|
||||
"There are %count% people groups in %country%. Check the map for the people groups on the border of this country as well.": "%country% में %count% लोगों के समूह हैं। इस देश की सीमा पर स्थित लोगों के समूहों के लिए मानचित्र भी देखें।",
|
||||
"Total": "संपूर्ण",
|
||||
"Type to search...": "खोजने के लिए टाइप करें..।",
|
||||
"Unengaged people groups": "अनलग्न लोगों के समूह",
|
||||
"WAGF Block": "WAGF ब्लॉक",
|
||||
"WAGF Region": "डब्ल्यूएजीएफ क्षेत्र",
|
||||
"people groups adopted": "लोगों के समूहों ने अपनाया",
|
||||
"people groups engaged": "प्रवेशित लोगों के समूह"
|
||||
}
|
||||
53
library/i18n/locales/pt/regions.json
Normal file
53
library/i18n/locales/pt/regions.json
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"%count% people groups in %countries% countries": "%count% grupos étnicos em %countries% países",
|
||||
"%region% has %count% people groups across %countries% countries. Each pin is coloured by how many people are praying for that people group.": "%region% tem %count% grupos étnicos em %countries% países. A cor de cada ponto indica quantas pessoas oram por esse grupo étnico.",
|
||||
"1+ People Praying": "1+ pessoas orando",
|
||||
"100+ People Praying": "100+ pessoas orando",
|
||||
"Adopted": "Aprovado",
|
||||
"Adoption Progress": "Progresso da adoção",
|
||||
"All Regions": "Todas as regiões",
|
||||
"Alternate Names": "Nomes alternativos",
|
||||
"Clear All": "Limpar tudo",
|
||||
"Countries": "Países",
|
||||
"Countries in this region": "Países desta região",
|
||||
"Country": "País",
|
||||
"Engaged": "Engajado",
|
||||
"Engagement Progress": "Progresso do engajamento",
|
||||
"Every unengaged people group adopted by a church committed to prayer, giving, and sending": "Cada grupo étnico não engajado adotado por uma igreja comprometida com a oração, contribuição e o envio",
|
||||
"Exact Search": "Pesquisa exata",
|
||||
"Find unreached people groups by region and country.": "Encontre grupos étnicos não alcançados por região e país.",
|
||||
"Full Profile": "Perfil completo",
|
||||
"Goal": "Meta",
|
||||
"Hide Filters": "Ocultar filtros",
|
||||
"Intercessors": "Intercessores",
|
||||
"Load More": "Carregar mais",
|
||||
"Loading results...": "A carregar resultados...",
|
||||
"Mobilize 100+ people praying daily for each DOXA people group": "Mobilizar mais de 100 pessoas para orar diariamente por cada grupo étnico DOXA",
|
||||
"No One Praying": "Ninguém orando",
|
||||
"No options found": "Não foram encontradas opções",
|
||||
"No unengaged people groups by 2033": "Nenhum grupo étnico não engajado até 2033",
|
||||
"Not Adopted": "Não adotado",
|
||||
"Other": "Outros",
|
||||
"Page Not Found": "Página não encontrada",
|
||||
"People Group": "Grupo étnico",
|
||||
"People Groups": "Grupos étnicos",
|
||||
"People committed to praying": "Pessoas comprometidas com a oração",
|
||||
"Population": "Aplicação",
|
||||
"Prayer Coverage": "Cobertura de oração",
|
||||
"Prayer coverage progress": "Progresso da cobertura de oração",
|
||||
"Regions": "Regiões",
|
||||
"Religion": "Religião",
|
||||
"Search names, country/continent, religions...": "Busque nomes, país/continente, religiões...",
|
||||
"See %region% dashboard": "Ver o painel: %region%",
|
||||
"See All": "Ver tudo",
|
||||
"Select": "Selecionar",
|
||||
"Show Filters": "Mostrar filtros",
|
||||
"There are %count% people groups in %country%. Check the map for the people groups on the border of this country as well.": "Existem %count% grupos étnicos em %country%. Verifique no mapa também os grupos étnicos na fronteira deste país.",
|
||||
"Total": "Total",
|
||||
"Type to search...": "Digite para pesquisar...",
|
||||
"Unengaged people groups": "Grupos étnicos não engajados",
|
||||
"WAGF Block": "Bloco WAGF",
|
||||
"WAGF Region": "Região da WAGF",
|
||||
"people groups adopted": "grupos étnicos adotados",
|
||||
"people groups engaged": "grupos étnicos engajados"
|
||||
}
|
||||
53
library/i18n/locales/ru/regions.json
Normal file
53
library/i18n/locales/ru/regions.json
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"%count% people groups in %countries% countries": "%count% групп людей в %countries% странах",
|
||||
"%region% has %count% people groups across %countries% countries. Each pin is coloured by how many people are praying for that people group.": "В регионе %region% — %count% групп людей в %countries% странах. Цвет каждой точки показывает, сколько человек молится за эту группу людей.",
|
||||
"1+ People Praying": "1+ человек молится",
|
||||
"100+ People Praying": "100+ человек молятся",
|
||||
"Adopted": "Принято",
|
||||
"Adoption Progress": "Прогресс принятия",
|
||||
"All Regions": "Все регионы",
|
||||
"Alternate Names": "Альтернативные названия",
|
||||
"Clear All": "Очистить всё",
|
||||
"Countries": "Страны",
|
||||
"Countries in this region": "Страны этого региона",
|
||||
"Country": "Страна",
|
||||
"Engaged": "Вовлечённый",
|
||||
"Engagement Progress": "Прогресс вовлечения",
|
||||
"Every unengaged people group adopted by a church committed to prayer, giving, and sending": "Каждая незадействованная народность принята церковью, приверженной молитве, пожертвованиям и посылке миссионеров",
|
||||
"Exact Search": "Точный поиск",
|
||||
"Find unreached people groups by region and country.": "Найдите недостигнутые группы людей по регионам и странам.",
|
||||
"Full Profile": "Полный профиль",
|
||||
"Goal": "Цель",
|
||||
"Hide Filters": "Скрыть фильтры",
|
||||
"Intercessors": "Ходатаи",
|
||||
"Load More": "Загрузить еще",
|
||||
"Loading results...": "Загрузка результатов...",
|
||||
"Mobilize 100+ people praying daily for each DOXA people group": "Мобилизуйте 100+ человек для ежедневной молитвы за каждую народность DOXA",
|
||||
"No One Praying": "Никто не молится",
|
||||
"No options found": "Вариантов не найдено",
|
||||
"No unengaged people groups by 2033": "К 2033 году не останется незадействованных народов",
|
||||
"Not Adopted": "Не принято",
|
||||
"Other": "Другое",
|
||||
"Page Not Found": "Страница не найдена",
|
||||
"People Group": "Группа людей",
|
||||
"People Groups": "Группы людей",
|
||||
"People committed to praying": "Люди, посвятившие себя молитве",
|
||||
"Population": "Население",
|
||||
"Prayer Coverage": "Молитвенное покрытие",
|
||||
"Prayer coverage progress": "Прогресс молитвенного покрытия",
|
||||
"Regions": "Регионы",
|
||||
"Religion": "Религия",
|
||||
"Search names, country/continent, religions...": "Поиск по именам, странам/континентам, религиям...",
|
||||
"See %region% dashboard": "Открыть панель: %region%",
|
||||
"See All": "Смотреть все",
|
||||
"Select": "Выбрать",
|
||||
"Show Filters": "Показать фильтры",
|
||||
"There are %count% people groups in %country%. Check the map for the people groups on the border of this country as well.": "Страна %country% насчитывает %count% групп людей. Проверьте также на карте группы людей, расположенные вдоль границ этой страны.",
|
||||
"Total": "Итого",
|
||||
"Type to search...": "Введите текст для поиска...",
|
||||
"Unengaged people groups": "Невовлечённые группы людей",
|
||||
"WAGF Block": "Блок WAGF",
|
||||
"WAGF Region": "Регион WAGF",
|
||||
"people groups adopted": "принятые группы людей",
|
||||
"people groups engaged": "вовлечённые группы людей"
|
||||
}
|
||||
|
|
@ -41,7 +41,18 @@ function buildRegionsDashboardElement() {
|
|||
return defineCustomElement(ProfileLoader, {
|
||||
configureApp(app) {
|
||||
app.use(createPinia())
|
||||
app.use(createAppI18n())
|
||||
// Install i18n AND hand the instance to ProfileLoader by reference, the way
|
||||
// doxa-simple-map/index.js:84-86 does. app.use() alone is not enough: createAppI18n()
|
||||
// resolves its locale ONCE from <html lang>, and every generated leaf page bakes
|
||||
// lang="en", so the element was permanently English no matter what the host asked for.
|
||||
// ProfileLoader drives appI18n.global.locale.value from profile-config.locale (its
|
||||
// "established contract", ProfileLoader.vue:148-169) — but only if appI18n is PROVIDED.
|
||||
// MEASURED before this line: with a Spanish catalogue present in the bundle and es set,
|
||||
// every string still rendered English, and silently, because the t() shim falls back to
|
||||
// its key rather than throwing.
|
||||
const i18n = createAppI18n()
|
||||
app.use(i18n)
|
||||
app.provide('appI18n', i18n)
|
||||
app.provide('profileModules', profileModules)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -24,9 +24,17 @@
|
|||
*/
|
||||
import { inject, ref, computed, onMounted, onBeforeUnmount, watch } from 'vue'
|
||||
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
|
||||
// ar renders right-to-left. RTL_LOCALES is the shared set and :dir on our own root is the
|
||||
// house pattern (doxa-simple-map.vue:18, :246, :1346) — an EMBEDDED element must not reach for
|
||||
// document.documentElement.dir, which belongs to the host page.
|
||||
import { RTL_LOCALES } from '@map/i18n/index.js'
|
||||
import { useDocumentFont, BEBAS_NEUE_FACES, POPPINS_FACES } from '@map/composables/useDocumentFont.js'
|
||||
import FilterDropdown from '@map/components/search/FilterDropdown.vue'
|
||||
import UupgsList from './src/UupgsList.vue'
|
||||
// Their strings, via their own English keys (see src/useRegionsI18n.js). The goal sentences,
|
||||
// the stat labels and the table headers are all t() calls on their page; hardcoding the
|
||||
// English was the last thing keeping this clone monolingual.
|
||||
import { useRegionsI18n } from './src/useRegionsI18n.js'
|
||||
// Region colours come from the shared strategy (check:one-palette), and REGION_ORDER is the
|
||||
// order its CVD separation was validated in — the legend must render in it for that to hold.
|
||||
import { PALETTE as REGION_PALETTE, WAGF_REGIONS, REGION_ORDER } from '@map/colors/wagf-region.js'
|
||||
|
|
@ -56,7 +64,13 @@ const cfg = computed(() => {
|
|||
try { return JSON.parse(props.profileConfig) || {} } catch { return {} }
|
||||
})
|
||||
const prayBaseUrl = computed(() => props.prayBaseUrl || cfg.value.prayBaseUrl || 'https://pray.doxa.life')
|
||||
const languageCode = computed(() => props.languageCode || cfg.value.languageCode || '')
|
||||
// The prayer API localises its LABELS from ?lang= — country names, region names, religion
|
||||
// names. MEASURED: with locale=ar the UI translated but every country in the table stayed
|
||||
// English, because this read only cfg.languageCode while the locale arrives as
|
||||
// cfg.locale (ProfileLoader's contract, ProfileLoader.vue:156 accepts locale OR lang).
|
||||
// Falling through to the same keys means translating the chrome also translates the data.
|
||||
const languageCode = computed(() =>
|
||||
props.languageCode || cfg.value.languageCode || cfg.value.locale || cfg.value.lang || '')
|
||||
const instanceId = computed(() => cfg.value.instanceId || '')
|
||||
// THE CAP-4 ASSET-PATH LESSON (BUG-UPG100MFE-ASSET-PATH, and it bit again here): DataTable
|
||||
// injects the vendored Tabulator from a path RELATIVE to the document. Nested inside the
|
||||
|
|
@ -80,6 +94,9 @@ useDocumentFont('Bebas Neue', BEBAS_NEUE_FACES, assetsBase.value)
|
|||
useDocumentFont('Poppins', POPPINS_FACES, assetsBase.value)
|
||||
|
||||
// ── state ───────────────────────────────────────────────────────────────────
|
||||
const { t, tf , locale } = useRegionsI18n()
|
||||
const isRTL = computed(() => RTL_LOCALES.has(locale.value))
|
||||
|
||||
const regions = ref([])
|
||||
// The rollup aggregates records away, and the map needs them one by one. Kept flat and
|
||||
// pre-parsed once here rather than re-fetched inside the map child, as DX-life's CountryMap
|
||||
|
|
@ -262,11 +279,11 @@ function percentOfSubject(count) {
|
|||
const stats = computed(() => {
|
||||
const s = subject.value.stats || emptyStats()
|
||||
const unengaged = Math.max(0, (s.count || 0) - (s.engaged || 0))
|
||||
const four = [{ label: 'People Groups', value: formatNumber(s.count) }]
|
||||
if (subject.value.kind === 'country') four.push({ label: 'Engaged', value: formatNumber(s.engaged) })
|
||||
else four.push({ label: 'Countries', value: formatNumber(subject.value.kind === 'all' ? totalCountryCount.value : (selectedRegion.value?.countryCount || 0)) })
|
||||
four.push({ label: 'Population', value: formatNumber(s.population) })
|
||||
four.push({ label: 'Unengaged people groups', value: formatNumber(unengaged) })
|
||||
const four = [{ label: t('People Groups'), value: formatNumber(s.count) }]
|
||||
if (subject.value.kind === 'country') four.push({ label: t('Engaged'), value: formatNumber(s.engaged) })
|
||||
else four.push({ label: t('Countries'), value: formatNumber(subject.value.kind === 'all' ? totalCountryCount.value : (selectedRegion.value?.countryCount || 0)) })
|
||||
four.push({ label: t('Population'), value: formatNumber(s.population) })
|
||||
four.push({ label: t('Unengaged people groups'), value: formatNumber(unengaged) })
|
||||
return four
|
||||
})
|
||||
|
||||
|
|
@ -297,12 +314,11 @@ const tableRows = computed(() => {
|
|||
})
|
||||
|
||||
const tableTitle = computed(() => {
|
||||
if (subject.value.kind === 'all') return 'All regions'
|
||||
if (subject.value.kind === 'country') return 'Countries in ' + (selectedRegion.value?.label || 'this region')
|
||||
return 'Countries in this region'
|
||||
if (subject.value.kind === 'all') return t('Regions')
|
||||
return t('Countries in this region')
|
||||
})
|
||||
|
||||
const tableFirstCol = computed(() => (subject.value.kind === 'all' ? 'Region' : 'Country'))
|
||||
const tableFirstCol = computed(() => (subject.value.kind === 'all' ? t('Region') : t('Country')))
|
||||
|
||||
// Their summary line, RegionDetail.vue:32-36 / the list's own. Kept as a %token% template for
|
||||
// the same reason tab 1 keeps it -- it is how their i18n carries this string.
|
||||
|
|
@ -312,9 +328,9 @@ const summaryLine = computed(() => {
|
|||
return formatNumber(s.count) + ' people groups \u00b7 ' + (selectedCountry.value?.region?.label || 'Other')
|
||||
}
|
||||
const countries = subject.value.kind === 'all' ? totalCountryCount.value : (selectedRegion.value?.countryCount || 0)
|
||||
return '%count% people groups in %countries% countries'
|
||||
.replace('%count%', formatNumber(s.count))
|
||||
.replace('%countries%', formatNumber(countries))
|
||||
return tf('%count% people groups in %countries% countries', {
|
||||
count: formatNumber(s.count), countries: formatNumber(countries),
|
||||
})
|
||||
})
|
||||
|
||||
// ★ THE NO-SELECTION QUESTION, answered from what DX-life actually does.
|
||||
|
|
@ -440,16 +456,15 @@ const mapZoom = computed(() => {
|
|||
// RegionDetail.vue:38-42 and CountryDetail.vue:29-33.
|
||||
const mapNote = computed(() => {
|
||||
if (selectedCountry.value) {
|
||||
return 'There are %count% people groups in %country%. Check the map for the people groups on the border of this country as well.'
|
||||
.replace('%count%', formatNumber(selectedCountry.value.count))
|
||||
.replace('%country%', selectedCountry.value.name)
|
||||
return tf('There are %count% people groups in %country%. Check the map for the people groups on the border of this country as well.', {
|
||||
count: formatNumber(selectedCountry.value.count), country: selectedCountry.value.name,
|
||||
})
|
||||
}
|
||||
const label = subject.value.title
|
||||
const countries = subject.value.kind === 'all' ? totalCountryCount.value : (selectedRegion.value?.countryCount || 0)
|
||||
return '%region% has %count% people groups across %countries% countries. Each pin is coloured by how many people are praying for that people group.'
|
||||
.replace('%region%', label)
|
||||
.replace('%count%', formatNumber(subject.value.stats.count))
|
||||
.replace('%countries%', formatNumber(countries))
|
||||
return tf('%region% has %count% people groups across %countries% countries. Each pin is coloured by how many people are praying for that people group.', {
|
||||
region: label, count: formatNumber(subject.value.stats.count), countries: formatNumber(countries),
|
||||
})
|
||||
})
|
||||
|
||||
// The country page's list is DX-life's own UupgsList, ported at src/UupgsList.vue — not a table
|
||||
|
|
@ -586,6 +601,11 @@ async function loadRegions() {
|
|||
}
|
||||
}
|
||||
|
||||
// A locale change has to REFETCH, not just re-render: the labels come from the API's ?lang=,
|
||||
// so without this the chrome switches language and the country names keep the language of
|
||||
// first paint.
|
||||
watch(languageCode, () => { loadRegions() })
|
||||
|
||||
onMounted(async () => {
|
||||
window.addEventListener(SELECT_EVENT, onSelect)
|
||||
if (URL_SYNC) window.addEventListener('popstate', onPopState)
|
||||
|
|
@ -785,7 +805,7 @@ useShadowStyles(
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="rd-root">
|
||||
<div class="rd-root" :dir="isRTL ? 'rtl' : 'ltr'">
|
||||
<!-- a landmark, so a screen reader can jump to the dashboard body; the app probe
|
||||
measured none on the page and it costs nothing to name the region. -->
|
||||
<main class="rd-scroll" aria-label="Region dashboard">
|
||||
|
|
@ -809,11 +829,11 @@ useShadowStyles(
|
|||
<!-- RegionDetail.vue:47-51 / CountryDetail.vue:37-45 — back-link, title, summary -->
|
||||
<div class="rd-head">
|
||||
<p v-if="!isAll" class="rd-backlink">
|
||||
<button type="button" class="rd-light-link" @click="clearSelection">← All Regions</button>
|
||||
<button type="button" class="rd-light-link" @click="clearSelection">← {{ t('All Regions') }}</button>
|
||||
</p>
|
||||
<h1 class="rd-title">{{ subject.title }}</h1>
|
||||
<p v-if="subject.kind === 'country'" class="rd-summary">
|
||||
{{ formatNumber(subject.stats.count) }} People Groups ·
|
||||
{{ formatNumber(subject.stats.count) }} {{ t('People Groups') }} ·
|
||||
<button
|
||||
type="button"
|
||||
class="rd-light-link"
|
||||
|
|
@ -906,44 +926,44 @@ useShadowStyles(
|
|||
<!-- RegionDetail.vue:88-141 — the three DOXA goal cards in their .switcher -->
|
||||
<div v-if="subject.kind === 'region'" class="rd-switcher">
|
||||
<div class="rd-card rd-card--secondary rd-goal-card">
|
||||
<h2 class="rd-h2">Prayer Coverage</h2>
|
||||
<h2 class="rd-h2">{{ t('Prayer Coverage') }}</h2>
|
||||
<div>
|
||||
<p class="rd-big">{{ formatNumber(subject.stats.peopleCommitted) }}</p>
|
||||
<p class="rd-big-label">People committed to praying</p>
|
||||
<p class="rd-big-label">{{ t('People committed to praying') }}</p>
|
||||
</div>
|
||||
<div class="rd-stack-xs">
|
||||
<div class="rd-bar rd-bar--segments" aria-label="Prayer coverage progress">
|
||||
<div class="rd-bar rd-bar--segments" :aria-label="t('Prayer coverage progress')">
|
||||
<div class="rd-seg rd-seg--full" :style="{ width: percentOfSubject(subject.stats.withFullPrayer) + '%' }" />
|
||||
<div class="rd-seg rd-seg--partial" :style="{ width: percentOfSubject(partialPrayerCount) + '%' }" />
|
||||
<div class="rd-seg rd-seg--none" :style="{ width: percentOfSubject(noPrayerCount) + '%' }" />
|
||||
</div>
|
||||
<div class="rd-legend">
|
||||
<span><i class="rd-key rd-key--full" />100+ People Praying: {{ formatNumber(subject.stats.withFullPrayer) }}</span>
|
||||
<span><i class="rd-key rd-key--partial" />1+ People Praying: {{ formatNumber(partialPrayerCount) }}</span>
|
||||
<span><i class="rd-key rd-key--none" />No One Praying: {{ formatNumber(noPrayerCount) }}</span>
|
||||
<span><i class="rd-key rd-key--full" />{{ t('100+ People Praying') }}: {{ formatNumber(subject.stats.withFullPrayer) }}</span>
|
||||
<span><i class="rd-key rd-key--partial" />{{ t('1+ People Praying') }}: {{ formatNumber(partialPrayerCount) }}</span>
|
||||
<span><i class="rd-key rd-key--none" />{{ t('No One Praying') }}: {{ formatNumber(noPrayerCount) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="rd-goal"><strong>Goal:</strong> Mobilize 100+ people praying daily for each DOXA people group</p>
|
||||
<p class="rd-goal"><strong>{{ t('Goal') }}:</strong> {{ t('Mobilize 100+ people praying daily for each DOXA people group') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="rd-card rd-goal-card">
|
||||
<h2 class="rd-h2">Adoption Progress</h2>
|
||||
<h2 class="rd-h2">{{ t('Adoption Progress') }}</h2>
|
||||
<div>
|
||||
<p class="rd-big">{{ formatNumber(subject.stats.adopted) }} / {{ formatNumber(subject.stats.count) }}</p>
|
||||
<p class="rd-big-label">people groups adopted</p>
|
||||
<p class="rd-big-label">{{ t('people groups adopted') }}</p>
|
||||
</div>
|
||||
<div class="rd-bar"><div class="rd-bar__slider" :style="{ width: adoptedPct + '%' }" /></div>
|
||||
<p class="rd-goal"><strong>Goal:</strong> Every unengaged people group adopted by a church committed to prayer, giving, and sending</p>
|
||||
<p class="rd-goal"><strong>{{ t('Goal') }}:</strong> {{ t('Every unengaged people group adopted by a church committed to prayer, giving, and sending') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="rd-card rd-goal-card">
|
||||
<h2 class="rd-h2">Engagement Progress</h2>
|
||||
<h2 class="rd-h2">{{ t('Engagement Progress') }}</h2>
|
||||
<div>
|
||||
<p class="rd-big">{{ formatNumber(subject.stats.engaged) }} / {{ formatNumber(subject.stats.count) }}</p>
|
||||
<p class="rd-big-label">people groups engaged</p>
|
||||
<p class="rd-big-label">{{ t('people groups engaged') }}</p>
|
||||
</div>
|
||||
<div class="rd-bar"><div class="rd-bar__slider" :style="{ width: engagedPct + '%' }" /></div>
|
||||
<p class="rd-goal"><strong>Goal:</strong> No unengaged people groups by 2033</p>
|
||||
<p class="rd-goal"><strong>{{ t('Goal') }}:</strong> {{ t('No unengaged people groups by 2033') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -955,10 +975,10 @@ useShadowStyles(
|
|||
<thead>
|
||||
<tr>
|
||||
<th>{{ tableFirstCol }}</th>
|
||||
<th>People Groups</th>
|
||||
<th>1+ People Praying</th>
|
||||
<th>Adopted</th>
|
||||
<th>Engaged</th>
|
||||
<th>{{ t('People Groups') }}</th>
|
||||
<th>{{ t('1+ People Praying') }}</th>
|
||||
<th>{{ t('Adopted') }}</th>
|
||||
<th>{{ t('Engaged') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@
|
|||
*/
|
||||
import { inject, ref, computed, onMounted, getCurrentInstance } from 'vue'
|
||||
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
|
||||
// ar renders right-to-left. RTL_LOCALES is the shared set and :dir on our own root is the
|
||||
// house pattern (doxa-simple-map.vue:18, :246, :1346) — an EMBEDDED element must not reach for
|
||||
// document.documentElement.dir, which belongs to the host page.
|
||||
import { RTL_LOCALES } from '@map/i18n/index.js'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useDocumentFont, BEBAS_NEUE_FACES, POPPINS_FACES } from '@map/composables/useDocumentFont.js'
|
||||
|
||||
const props = defineProps({
|
||||
|
|
@ -61,10 +66,18 @@ const cfg = computed(() => {
|
|||
try { return JSON.parse(props.profileConfig) || {} } catch { return {} }
|
||||
})
|
||||
const prayBaseUrl = computed(() => props.prayBaseUrl || cfg.value.prayBaseUrl || 'https://pray.doxa.life')
|
||||
const languageCode = computed(() => props.languageCode || cfg.value.languageCode || '')
|
||||
// The prayer API localises its LABELS from ?lang= — country names, region names, religion
|
||||
// names. MEASURED: with locale=ar the UI translated but every country in the table stayed
|
||||
// English, because this read only cfg.languageCode while the locale arrives as
|
||||
// cfg.locale (ProfileLoader's contract, ProfileLoader.vue:156 accepts locale OR lang).
|
||||
// Falling through to the same keys means translating the chrome also translates the data.
|
||||
const languageCode = computed(() =>
|
||||
props.languageCode || cfg.value.languageCode || cfg.value.locale || cfg.value.lang || '')
|
||||
// Where the shared asset tree is served from: the generator bakes it per page depth and the
|
||||
// composite forwards it via INHERITED_PAGE_KEYS. Needed for the vendored @font-face path below.
|
||||
const assetsBase = computed(() => cfg.value.assetsBase || './assets')
|
||||
const { locale: i18nLocale } = useI18n()
|
||||
const isRTL = computed(() => RTL_LOCALES.has(i18nLocale.value))
|
||||
|
||||
// ★ REGISTER BEBAS NEUE AT DOCUMENT LEVEL, not in the shadow root: @font-face is document-scoped
|
||||
// and a face declared inside a shadow root is ignored. This profile registers it too, and not
|
||||
|
|
@ -356,7 +369,7 @@ useShadowStyles(
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="rl-root">
|
||||
<div class="rl-root" :dir="isRTL ? 'rtl' : 'ltr'">
|
||||
<div class="rl-scroll">
|
||||
<div v-if="loading" class="rl-status">Loading regions…</div>
|
||||
<div v-else-if="loadFailed" class="rl-status rl-failed" role="status">
|
||||
|
|
|
|||
|
|
@ -28,7 +28,17 @@
|
|||
*/
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
|
||||
// ar renders right-to-left. RTL_LOCALES is the shared set and :dir on our own root is the
|
||||
// house pattern (doxa-simple-map.vue:18, :246, :1346) — an EMBEDDED element must not reach for
|
||||
// document.documentElement.dir, which belongs to the host page.
|
||||
import { RTL_LOCALES } from '@map/i18n/index.js'
|
||||
import FilterDropdown from '@map/components/search/FilterDropdown.vue'
|
||||
// Their strings, via their own keys — see useRegionsI18n. buildUupgListTranslations.ts is the
|
||||
// authoritative list of what this component's labels are called, and porting it corrected
|
||||
// several I had written from the markup instead: the rop1 filter is 'People Group' not
|
||||
// 'Affinity Bloc', the search placeholder is the full
|
||||
// 'Search names, country/continent, religions...', and prayer_coverage reads 'Intercessors'.
|
||||
import { useRegionsI18n } from './useRegionsI18n.js'
|
||||
|
||||
const props = defineProps({
|
||||
// ISO-3. Their `lockCountryCode`: the country filter is pre-applied and its dropdown hidden.
|
||||
|
|
@ -45,6 +55,28 @@ const props = defineProps({
|
|||
// Their field list, verbatim (UupgsList.vue:388).
|
||||
const FIELDS = 'name,slug,wagf_region,wagf_block,country_code,rop1,religion,has_photo,image_url,adopted_by_churches,imb_alternate_name,engagement_status,people_committed,location_description'
|
||||
|
||||
const { t , locale } = useRegionsI18n()
|
||||
const isRTL = computed(() => RTL_LOCALES.has(locale.value))
|
||||
|
||||
// buildUupgListTranslations (app/utils/uupgListTranslations.ts), ported key for key. Their
|
||||
// component takes this as a `t` prop; ours resolves it from the catalogue at render time so a
|
||||
// locale change re-renders without re-mounting.
|
||||
const T = computed(() => ({
|
||||
search: t('Search names, country/continent, religions...'),
|
||||
show_filters: t('Show Filters'),
|
||||
hide_filters: t('Hide Filters'),
|
||||
clear_all: t('Clear All'),
|
||||
type_to_search: t('Type to search...'),
|
||||
wagf_region: t('WAGF Region'),
|
||||
wagf_block: t('WAGF Block'),
|
||||
rop1: t('People Group'),
|
||||
religion: t('Religion'),
|
||||
loading: t('Loading results...'),
|
||||
load_more: t('Load More'),
|
||||
full_profile: t('Full Profile'),
|
||||
people_groups: t('People Groups'),
|
||||
}))
|
||||
|
||||
const rows = ref([])
|
||||
const loading = ref(true)
|
||||
const failed = ref(false)
|
||||
|
|
@ -91,10 +123,10 @@ function optionsFor(key) {
|
|||
return [...m.values()].sort((a, b) => a.label.localeCompare(b.label))
|
||||
}
|
||||
const filterDefs = computed(() => [
|
||||
{ name: 'wagf_region', label: 'WAGF Region' },
|
||||
{ name: 'wagf_block', label: 'WAGF Block' },
|
||||
{ name: 'rop1', label: 'Affinity Bloc' },
|
||||
{ name: 'religion', label: 'Religion' },
|
||||
{ name: 'wagf_region', label: T.value.wagf_region },
|
||||
{ name: 'wagf_block', label: T.value.wagf_block },
|
||||
{ name: 'rop1', label: T.value.rop1 },
|
||||
{ name: 'religion', label: T.value.religion },
|
||||
].map((f) => ({ ...f, options: optionsFor(f.name) })))
|
||||
|
||||
const activeChips = computed(() =>
|
||||
|
|
@ -207,15 +239,15 @@ useShadowStyles(
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ul-root">
|
||||
<div class="ul-root" :dir="isRTL ? 'rtl' : 'ltr'">
|
||||
<div class="filters">
|
||||
<div class="search-box">
|
||||
<svg class="search-icon" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<circle cx="11" cy="11" r="7" />
|
||||
<path d="M20 20l-3.5-3.5" stroke-linecap="round" />
|
||||
</svg>
|
||||
<label class="sr-only" :for="'ul-search'">Search</label>
|
||||
<input id="ul-search" v-model="searchTerm" type="search" placeholder="Search" aria-label="Search people groups">
|
||||
<label class="sr-only" for="ul-search">{{ T.search }}</label>
|
||||
<input id="ul-search" v-model="searchTerm" type="search" :placeholder="T.search" :aria-label="T.search">
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -224,7 +256,7 @@ useShadowStyles(
|
|||
:aria-expanded="filtersExpanded ? 'true' : 'false'"
|
||||
@click="filtersExpanded = !filtersExpanded"
|
||||
>
|
||||
{{ filtersExpanded ? 'Hide Filters' : 'Show Filters' }}
|
||||
{{ filtersExpanded ? T.hide_filters : T.show_filters }}
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true">
|
||||
<path d="M2 4l4 4 4-4" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
|
|
@ -238,7 +270,7 @@ useShadowStyles(
|
|||
:name="f.name"
|
||||
:options="f.options"
|
||||
:value="active[f.name]?.value || ''"
|
||||
placeholder="Type to search…"
|
||||
:placeholder="T.type_to_search"
|
||||
@filter-change="onFilterChange"
|
||||
@filter-clear="onFilterClear"
|
||||
/>
|
||||
|
|
@ -247,14 +279,14 @@ useShadowStyles(
|
|||
<div v-if="activeChips.length" class="filters__active">
|
||||
<span v-for="c in activeChips" :key="c.name" class="filter-chip">
|
||||
{{ c.label }}
|
||||
<button type="button" class="filter-chip__remove" :aria-label="'Remove ' + c.label" @click="onFilterClear({ name: c.name })">×</button>
|
||||
<button type="button" class="filter-chip__remove" :aria-label="c.label" @click="onFilterClear({ name: c.name })">×</button>
|
||||
</span>
|
||||
<button type="button" class="filters__clear-all" @click="clearAll">Clear all</button>
|
||||
<button type="button" class="filters__clear-all" @click="clearAll">{{ T.clear_all }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p v-if="!loading && !failed" class="ul-count">
|
||||
{{ filtered.length.toLocaleString('en-US') }} people groups<span v-if="countryName"> in {{ countryName }}</span>
|
||||
{{ filtered.length.toLocaleString('en-US') }} {{ T.people_groups }}<span v-if="countryName"> · {{ countryName }}</span>
|
||||
</p>
|
||||
|
||||
<div v-if="failed" class="ul-status ul-failed" role="status">
|
||||
|
|
@ -272,11 +304,11 @@ useShadowStyles(
|
|||
</p>
|
||||
</div>
|
||||
<p v-if="p.location_description" class="uupg__content">{{ p.location_description }}</p>
|
||||
<a class="uupg__more-button" :href="profileUrl(p)">Full profile</a>
|
||||
<a class="uupg__more-button" :href="profileUrl(p)">{{ T.full_profile }}</a>
|
||||
</div>
|
||||
<div v-if="loading" class="loading">Loading…</div>
|
||||
<div v-if="loading" class="loading">{{ T.loading }}</div>
|
||||
</div>
|
||||
|
||||
<button v-if="showMore" type="button" class="ul-more" @click="loadMore">Load more</button>
|
||||
<button v-if="showMore" type="button" class="ul-more" @click="loadMore">{{ T.load_more }}</button>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
53
plugin-designs/regions-dashboard/src/useRegionsI18n.js
Normal file
53
plugin-designs/regions-dashboard/src/useRegionsI18n.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* useRegionsI18n.js — t() for the regions clone, keyed by DX-life's own English strings.
|
||||
*
|
||||
* WHY A SHIM AND NOT PLAIN t(). DX-life's components call t('Prayer Coverage') — the English
|
||||
* sentence IS the key, which is how their common.json is built. Our catalogue lands at
|
||||
* messages[locale].regions (library/i18n/index.js composes the namespace from the FILENAME), so
|
||||
* the vue-i18n path would be 'regions.Prayer Coverage'. That breaks for any key containing a dot,
|
||||
* and several do — 'Loading results...', 'Type to search...' — because '.' is vue-i18n's key
|
||||
* separator. tm() returns the whole namespace object instead, so we can index it by the literal
|
||||
* string and call their key exactly as they wrote it. The port stays 1:1 with their source, which
|
||||
* is the point.
|
||||
*
|
||||
* FALLS BACK TO THE KEY. A missing string renders the English sentence rather than 'regions.Foo'.
|
||||
* That matters here: our repo ships 11 locales and DX-life translates 8, so it/ro/zh have no
|
||||
* regions.json at all and resolve through this fallback (library/i18n README: a missing area file
|
||||
* is fine, fallbackLocale 'en' handles it).
|
||||
*
|
||||
* PLACEMENT: plugin-local on purpose. It is four lines shaped entirely by how THESE pages were
|
||||
* written, and the repo's rule is to default to the plugin folder and promote to library/ only
|
||||
* with a named second consumer. If another bundle ever clones DX-life pages the same way, promote
|
||||
* it then.
|
||||
*/
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
export function useRegionsI18n() {
|
||||
const { tm, rt, locale } = useI18n()
|
||||
// tm() gives the namespace object; rt() resolves a message from it.
|
||||
const catalogue = computed(() => {
|
||||
try { return tm('regions') || {} } catch (_) { return {} }
|
||||
})
|
||||
|
||||
/** t('Prayer Coverage') — their call, verbatim. Unknown key renders as itself. */
|
||||
function t(key) {
|
||||
const v = catalogue.value?.[key]
|
||||
if (v == null) return key
|
||||
try { return rt(v) } catch (_) { return typeof v === 'string' ? v : key }
|
||||
}
|
||||
|
||||
/**
|
||||
* Their %token% substitution (RegionDetail.vue:32-42 explains why they avoid vue-i18n's {0}
|
||||
* slots: it empties brace slots when a key resolves with no args and leaves them literal when
|
||||
* the key is missing, so neither path interpolates). Same manual replace, so the same strings
|
||||
* work in every locale.
|
||||
*/
|
||||
function tf(key, tokens = {}) {
|
||||
let out = t(key)
|
||||
for (const [k, v] of Object.entries(tokens)) out = out.replace('%' + k + '%', String(v))
|
||||
return out
|
||||
}
|
||||
|
||||
return { t, tf, locale }
|
||||
}
|
||||
8
public/doxa-maps/manifest.json
generated
8
public/doxa-maps/manifest.json
generated
|
|
@ -838,7 +838,7 @@
|
|||
"bundle": "regions-dashboard",
|
||||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"output": "doxa-maps/regions-dashboard/regions-dashboard.js",
|
||||
"integrity": "sha384-gzqCEAoNJjv96WjkGUzVtzvyDAK+vLL7/hX/aCJf5ikDznGXSkbsKYORSBw5GBwG",
|
||||
"integrity": "sha384-WGTEO4UGtTqTkbrv4VjKXBejcEHt6EYnem9sSu+xcDmxTAWDpBQ6DwjLfgubA7k+",
|
||||
"elements": [
|
||||
"regions-dashboard"
|
||||
],
|
||||
|
|
@ -858,7 +858,7 @@
|
|||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./regions-dashboard/region-dashboards/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-gzqCEAoNJjv96WjkGUzVtzvyDAK+vLL7/hX/aCJf5ikDznGXSkbsKYORSBw5GBwG\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-WGTEO4UGtTqTkbrv4VjKXBejcEHt6EYnem9sSu+xcDmxTAWDpBQ6DwjLfgubA7k+\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -872,7 +872,7 @@
|
|||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./regions-dashboard/regions-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-gzqCEAoNJjv96WjkGUzVtzvyDAK+vLL7/hX/aCJf5ikDznGXSkbsKYORSBw5GBwG\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-WGTEO4UGtTqTkbrv4VjKXBejcEHt6EYnem9sSu+xcDmxTAWDpBQ6DwjLfgubA7k+\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -886,7 +886,7 @@
|
|||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./regions-dashboard/regions-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-gzqCEAoNJjv96WjkGUzVtzvyDAK+vLL7/hX/aCJf5ikDznGXSkbsKYORSBw5GBwG\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-WGTEO4UGtTqTkbrv4VjKXBejcEHt6EYnem9sSu+xcDmxTAWDpBQ6DwjLfgubA7k+\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
|
||||
<script src="../regions-dashboard.js" integrity="sha384-gzqCEAoNJjv96WjkGUzVtzvyDAK+vLL7/hX/aCJf5ikDznGXSkbsKYORSBw5GBwG" crossorigin="anonymous"></script>
|
||||
<script src="../regions-dashboard.js" integrity="sha384-WGTEO4UGtTqTkbrv4VjKXBejcEHt6EYnem9sSu+xcDmxTAWDpBQ6DwjLfgubA7k+" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
|
||||
<script src="../regions-dashboard.js" integrity="sha384-gzqCEAoNJjv96WjkGUzVtzvyDAK+vLL7/hX/aCJf5ikDznGXSkbsKYORSBw5GBwG" crossorigin="anonymous"></script>
|
||||
<script src="../regions-dashboard.js" integrity="sha384-WGTEO4UGtTqTkbrv4VjKXBejcEHt6EYnem9sSu+xcDmxTAWDpBQ6DwjLfgubA7k+" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
|
||||
<script src="../regions-dashboard.js" integrity="sha384-gzqCEAoNJjv96WjkGUzVtzvyDAK+vLL7/hX/aCJf5ikDznGXSkbsKYORSBw5GBwG" crossorigin="anonymous"></script>
|
||||
<script src="../regions-dashboard.js" integrity="sha384-WGTEO4UGtTqTkbrv4VjKXBejcEHt6EYnem9sSu+xcDmxTAWDpBQ6DwjLfgubA7k+" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
Loading…
Reference in a new issue