update data models

This commit is contained in:
Fabio Bonetti
2026-02-11 16:31:03 +01:00
parent e2b308c562
commit 2c012f132a
10 changed files with 333 additions and 399 deletions

View File

@@ -7,4 +7,4 @@ COPY config/ /var/www/html/user/config/
COPY pages/ /var/www/html/user/pages/ COPY pages/ /var/www/html/user/pages/
# Fix permissions # Fix permissions
RUN chown -R www-data:www-data /var/www/html/user/ RUN chown -R www-data:www-data /var/www/html/user/

View File

@@ -1,7 +1,3 @@
# Station Blueprint
# Defines a content module/station for the media guide
# /user/blueprints/station.yaml
title: Station title: Station
'@extends': '@extends':
type: default type: default
@@ -14,7 +10,6 @@ form:
active: 1 active: 1
fields: fields:
# Station Info Tab
station_info: station_info:
type: tab type: tab
title: Station Info title: Station Info
@@ -22,10 +17,8 @@ form:
header.station_id: header.station_id:
type: text type: text
label: Station ID label: Station ID
help: Unique identifier for this station (used in QR codes)
validate: validate:
required: true required: true
pattern: '^[a-z0-9-]+$'
header.station_type: header.station_type:
type: select type: select
@@ -37,11 +30,6 @@ form:
navigation: Navigation navigation: Navigation
default: exhibit default: exhibit
header.exhibition_ref:
type: text
label: Exhibition Reference
help: Reference to parent exhibition
header.active: header.active:
type: toggle type: toggle
label: Active label: Active
@@ -55,127 +43,3 @@ form:
type: number type: number
label: Display Order label: Display Order
default: 0 default: 0
# Multilingual Content Tab
content_tab:
type: tab
title: Content
fields:
header.title:
type: multilang
label: Title
languages: true
header.description:
type: multilang
label: Short Description
type: textarea
languages: true
# Components Tab
components_tab:
type: tab
title: Components
fields:
header.components:
type: list
label: Content Components
style: vertical
btnLabel: Add Component
collapsed: true
fields:
.type:
type: select
label: Component Type
options:
heading: Heading
text: Text Block
video: Video Player
audio: Audio Player
tabs: Tabbed Content
slideshow: Slideshow
.id:
type: text
label: Component ID
help: Unique ID for this component
# Heading specific
.level:
type: select
label: Heading Level
options:
h1: H1
h2: H2
h3: H3
# Text/Heading content (multilingual)
.content:
type: editor
label: Content
# Video specific
.video_src:
type: filepicker
label: Video File
folder: 'user://media/videos'
accept:
- video/*
.video_poster:
type: filepicker
label: Video Poster
folder: 'user://media/images'
accept:
- image/*
# Audio specific
.audio_src:
type: filepicker
label: Audio File
folder: 'user://media/audio'
accept:
- audio/*
.caption:
type: text
label: Caption
# Tabs specific
.tabs:
type: list
label: Tabs
fields:
.title:
type: text
label: Tab Title
.content:
type: editor
label: Tab Content
# Slideshow specific
.slides:
type: list
label: Slides
fields:
.image:
type: filepicker
label: Slide Image
folder: 'user://media/images'
accept:
- image/*
.caption:
type: text
label: Slide Caption
# Media Tab
media_tab:
type: tab
title: Media Files
fields:
header.media_folder:
type: text
label: Media Folder
help: Folder containing media files for this station
placeholder: /media/station-id

View File

@@ -1,6 +1,3 @@
# Grav Site Configuration
# /user/config/site.yaml
title: EU Media Guide CMS title: EU Media Guide CMS
default_lang: en default_lang: en
@@ -10,18 +7,3 @@ author:
metadata: metadata:
description: Multilingual Media Guide Content Management System description: Multilingual Media Guide Content Management System
taxonomies:
- category
- tag
- exhibition
- station_type
# Custom fields for the media guide
mediaguide:
exhibitions: []
station_types:
- intro
- exhibit
- information
- navigation

View File

@@ -1,73 +1,45 @@
# Grav System Configuration
# /user/config/system.yaml
languages: languages:
supported: supported:
# EU Protocol Order (alphabetical by native name) - bg
- bg # български (Bulgarian) - cs
- es # español (Spanish) - da
- cs # čeština (Czech) - de
- da # dansk (Danish) - el
- de # Deutsch (German) - en
- et # eesti (Estonian) - es
- el # ελληνικά (Greek) - et
- en # English - fi
- fr # français (French) - fr
- ga # Gaeilge (Irish) - ga
- hr # hrvatski (Croatian) - hr
- it # italiano (Italian) - hu
- lv # latviešu (Latvian) - it
- lt # lietuvių (Lithuanian) - lt
- hu # magyar (Hungarian) - lv
- mt # Malti (Maltese) - mt
- nl # Nederlands (Dutch) - nl
- pl # polski (Polish) - pl
- pt # português (Portuguese) - pt
- ro # română (Romanian) - ro
- sk # slovenčina (Slovak) - sk
- sl # slovenščina (Slovenian) - sl
- fi # suomi (Finnish) - sv
- sv # svenska (Swedish)
default_lang: en default_lang: en
include_default_lang: true include_default_lang: true
translations: true translations: true
translations_fallback: true translations_fallback: true
session_store_active: false
http_accept_language: true http_accept_language: true
override_locale: false
pages: pages:
theme: quark theme: quark
markdown: markdown:
extra: true extra: true
types:
- txt
- xml
- html
- htm
- json
- rss
- atom
cache: cache:
enabled: false # Disable during development
check:
method: file
driver: auto
prefix: 'g'
lifetime: 604800
debugger:
enabled: false enabled: false
shutdown: driver: auto
close_connection: true
twig: twig:
cache: false # Disable during development cache: false
debug: true debug: true
auto_reload: true auto_reload: true
autoescape: false
assets:
css_pipeline: false
js_pipeline: false

View File

@@ -16,4 +16,4 @@ services:
volumes: volumes:
grav_accounts: grav_accounts:
grav_data: grav_data:

View File

@@ -0,0 +1,100 @@
---
title: European Concerns - Posters
station_id: 5a0c1432bcb943c304dae4b7
station_type: exhibit
active: true
order: 2
title_translations:
en: Select a poster to read the message!
fr: Faire défiler et sélectionner une affiche
de: Blättern Sie und wählen Sie ein Poster
nl: Blader en kies een poster
it: Sfoglia e seleziona un manifesto
es: Hojee y seleccione un cartel
pt: Percorra a galeria e seleccione um cartaz
bg: Разгледайте и изберете плакат
cs: Vyberte si plakát z nabídky
da: Bladr og vælg en plakat
el: Ξεφυλλίστε και διαλέξτε μία αφίσα
et: Lehitse ja vali plakat
fi: Selaa ja valitse juliste
ga: Féach tríd na postaeir agus roghnaigh ceann
hr: Izaberite poster za čitanje poruke!
hu: Lapozzon és válasszon ki egy plakátot!
lt: Paslinkite kadrus ir pasirinkite plakatą
lv: Pāršķirstiet sarakstu un izvēlieties plakātu.
mt: Qalleb fil-posters u agħżel wieħed
pl: Przewiń listę i wybierz plakat
ro: Selectaţi o imagine
sk: Listujte a vyberte si plagát
sl: Pobrskajte in izberite poster
sv: Bläddra och välj en affisch
description_translations:
en: European concerns
fr: Les préoccupations des européens
de: Europäische Themen
nl: Europese kwesties
it: Gli affari europei
es: Preocupaciones europeas
pt: Temas europeus
bg: Европейски въпроси
cs: Evropská problematika
da: Europæiske anliggender
el: Προβληματισμοί των Ευρωπαίων
et: Euroopa mured
fi: Eurooppalaisille tärkeitä asioita
ga: Cúiseanna imní don Eoraip
hr: Europska pitanja
hu: Európai gondok
lt: Europos uždaviniai
lv: Eiropiešiem svarīgi jautājumi
mt: Kwistjonijiet Ewropej
pl: Troski Europejczyków
ro: Aspecte importante pentru Europa
sk: Európske témy
sl: Evropska vprašanja
sv: Viktiga frågor för EU
components:
- type: heading
id: concerns-heading
level: h1
content:
en: European concerns
fr: Les préoccupations des européens
de: Europäische Themen
nl: Europese kwesties
it: Gli affari europei
es: Preocupaciones europeas
- type: text
id: concerns-intro
content:
en: <p>Select a poster to read the message and learn about the issues that matter to Europeans.</p>
fr: <p>Sélectionnez une affiche pour lire le message et découvrir les sujets qui préoccupent les Européens.</p>
de: <p>Wählen Sie ein Poster aus, um die Botschaft zu lesen und mehr über die Themen zu erfahren, die den Europäern wichtig sind.</p>
nl: <p>Selecteer een poster om het bericht te lezen en meer te weten te komen over de kwesties die belangrijk zijn voor Europeanen.</p>
- type: slideshow
id: posters-slideshow
slides:
- image: /images/posters/poster1.jpg
caption:
en: European concerns poster 1
fr: Affiche des préoccupations européennes 1
de: Europäische Themen Poster 1
- image: /images/posters/poster2.jpg
caption:
en: European concerns poster 2
fr: Affiche des préoccupations européennes 2
de: Europäische Themen Poster 2
- image: /images/posters/poster3.jpg
caption:
en: European concerns poster 3
fr: Affiche des préoccupations européennes 3
de: Europäische Themen Poster 3
---
Interactive poster exhibition about European concerns and issues.

View File

@@ -0,0 +1,100 @@
---
title: Welcome - Touch to Start
station_id: 5a0034ab86c7c57110ae0a43
station_type: intro
active: true
order: 1
title_translations:
en: Please touch here to start
fr: Veuillez appuyer ici pour démarrer
de: Wiedergabe bitte hier drücken
nl: Druk hier om te beginnen
it: Premete qui per iniziare
es: Pulse aquí para comenzar
pt: Por favor toque aqui para começar
bg: Моля, докоснете тук, за да започнете
cs: Zde stiskněte a spusťte přehrávání
da: Tryk venligst her for at starte
el: Παρακαλούμε πιέστε εδώ για να ξεκινήσετε
et: Alustamiseks vajutage palun siia
fi: Aloita koskettamalla tästä
ga: Leag do mhéar anseo le tosú
hr: Dodirnite ovdje zu početak
hu: A kezdéshez nyomja meg ezt a gombot!
lt: Jei norite pradėti, palieskite čia
lv: Lūdzu, pieskarieties šeit, lai sāktu
mt: Jekk jogħġbok agħfas hawn biex tibda
pl: Dotknij, aby rozpocząć
ro: Pentru a începe vă rugăm să apăsaţi aici
sk: Dotykom na tomto mieste sa spustí prehrávanie
sl: Za začetek pritisnite tukaj
sv: Peka här för att starta
description_translations:
en: Welcome video introduction to the European Parliament
fr: Vidéo d'introduction au Parlement européen
de: Einführungsvideo zum Europäischen Parlament
nl: Introductievideo Europees Parlement
it: Video introduttivo al Parlamento europeo
es: Vídeo de introducción al Parlamento Europeo
components:
- type: heading
id: welcome-heading
level: h1
content:
en: Please touch here to start
fr: Veuillez appuyer ici pour démarrer
de: Wiedergabe bitte hier drücken
nl: Druk hier om te beginnen
it: Premete qui per iniziare
es: Pulse aquí para comenzar
pt: Por favor toque aqui para começar
bg: Моля, докоснете тук, за да започнете
cs: Zde stiskněte a spusťte přehrávání
da: Tryk venligst her for at starte
el: Παρακαλούμε πιέστε εδώ για να ξεκινήσετε
et: Alustamiseks vajutage palun siia
fi: Aloita koskettamalla tästä
ga: Leag do mhéar anseo le tosú
hr: Dodirnite ovdje zu početak
hu: A kezdéshez nyomja meg ezt a gombot!
lt: Jei norite pradėti, palieskite čia
lv: Lūdzu, pieskarieties šeit, lai sāktu
mt: Jekk jogħġbok agħfas hawn biex tibda
pl: Dotknij, aby rozpocząć
ro: Pentru a începe vă rugăm să apăsaţi aici
sk: Dotykom na tomto mieste sa spustí prehrávanie
sl: Za začetek pritisnite tukaj
sv: Peka här för att starta
- type: video
id: intro-video
src:
en: /videos/cms_7388676871120926234.mp4
bg: /videos/cms_3046071971567520684.mp4
cs: /videos/cms_2771914627657443657.mp4
da: /videos/cms_1785836764716686675.mp4
de: /videos/cms_6408694982373705979.mp4
el: /videos/cms_6207215930569761535.mp4
es: /videos/cms_1390123522791732069.mp4
et: /videos/cms_7268116320585483033.mp4
fi: /videos/cms_340857536848363735.mp4
fr: /videos/cms_766311708362015941.mp4
ga: /videos/cms_157201896609093943.mp4
hu: /videos/cms_1664578063593428062.mp4
it: /videos/cms_8746182921133737875.mp4
pl: /videos/cms_3435441211807278988.mp4
pt: /videos/cms_8781957532829411669.mp4
poster: /images/video-poster.jpg
caption:
en: Welcome to the European Parliament
fr: Bienvenue au Parlement européen
de: Willkommen im Europäischen Parlament
nl: Welkom bij het Europees Parlement
it: Benvenuti al Parlamento europeo
es: Bienvenidos al Parlamento Europeo
---
Welcome video content - Introduction to the European Parliament visitor center.

View File

@@ -4,7 +4,4 @@ description: Exports Grav content to JSON format for the React media guide app
icon: file-code icon: file-code
author: author:
name: Media Guide Team name: Media Guide Team
homepage: https://github.com/your-org/mediaguide
keywords: json, export, api, multilingual
bugs: https://github.com/your-org/mediaguide/issues
license: MIT license: MIT

View File

@@ -2,15 +2,7 @@
namespace Grav\Plugin; namespace Grav\Plugin;
use Grav\Common\Plugin; use Grav\Common\Plugin;
use RocketTheme\Toolbox\Event\Event;
use Grav\Common\Page\Page;
use Grav\Common\Grav;
/**
* JSON Export Plugin
*
* Exports content from Grav CMS to JSON format for the React media guide app
*/
class JsonExportPlugin extends Plugin class JsonExportPlugin extends Plugin
{ {
public static function getSubscribedEvents() public static function getSubscribedEvents()
@@ -22,14 +14,10 @@ class JsonExportPlugin extends Plugin
public function onPluginsInitialized() public function onPluginsInitialized()
{ {
// Check if we're in admin context
if ($this->isAdmin()) { if ($this->isAdmin()) {
$this->enable([ return;
'onAdminTaskExecute' => ['onAdminTaskExecute', 0]
]);
} }
// Enable API endpoint
$this->enable([ $this->enable([
'onPagesInitialized' => ['onPagesInitialized', 0] 'onPagesInitialized' => ['onPagesInitialized', 0]
]); ]);
@@ -38,31 +26,24 @@ class JsonExportPlugin extends Plugin
public function onPagesInitialized() public function onPagesInitialized()
{ {
$uri = $this->grav['uri']; $uri = $this->grav['uri'];
// API endpoints
if (strpos($uri->path(), '/api/') === 0) {
$this->handleApiRequest($uri);
}
}
private function handleApiRequest($uri)
{
$path = $uri->path(); $path = $uri->path();
$lang = $uri->param('lang') ?? 'en';
if (strpos($path, '/api/') !== 0) {
return;
}
$lang = $_GET['lang'] ?? 'en';
header('Content-Type: application/json'); header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Origin: *');
// GET /api/stations - List all stations
if ($path === '/api/stations') { if ($path === '/api/stations') {
echo json_encode($this->getStationsList($lang), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); echo json_encode($this->getStationsList($lang), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
exit; exit;
} }
// GET /api/station/{id} - Get single station if (preg_match('#^/api/station/([a-zA-Z0-9-]+)$#', $path, $matches)) {
if (preg_match('#^/api/station/([a-z0-9-]+)$#', $path, $matches)) { $station = $this->getStation($matches[1], $lang);
$stationId = $matches[1];
$station = $this->getStation($stationId, $lang);
if ($station) { if ($station) {
echo json_encode($station, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); echo json_encode($station, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
} else { } else {
@@ -72,18 +53,10 @@ class JsonExportPlugin extends Plugin
exit; exit;
} }
// GET /api/translations - Get UI translations
if ($path === '/api/translations') { if ($path === '/api/translations') {
echo json_encode($this->getTranslations($lang), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); echo json_encode($this->getTranslations($lang), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
exit; exit;
} }
// GET /api/export - Export all content to JSON files
if ($path === '/api/export') {
$result = $this->exportAllToJson();
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
exit;
}
} }
private function getStationsList($lang) private function getStationsList($lang)
@@ -92,22 +65,21 @@ class JsonExportPlugin extends Plugin
$stations = []; $stations = [];
foreach ($pages->all() as $page) { foreach ($pages->all() as $page) {
if ($page->template() === 'station' && $page->header()->active) { if ($page->template() === 'station') {
$header = $page->header(); $header = $page->header();
if (!($header->active ?? true)) continue;
$stations[] = [ $stations[] = [
'id' => $header->station_id ?? $page->slug(), 'id' => $header->station_id ?? $page->slug(),
'type' => $header->station_type ?? 'exhibit', 'type' => $header->station_type ?? 'exhibit',
'title' => $this->getTranslatedField($header->title_translations ?? [], $lang), 'title' => $this->getTranslated($header->title_translations ?? [], $lang),
'description' => $this->getTranslatedField($header->description_translations ?? [], $lang), 'description' => $this->getTranslated($header->description_translations ?? [], $lang),
'order' => $header->order ?? 0, 'order' => $header->order ?? 0
'active' => $header->active ?? true
]; ];
} }
} }
// Sort by order
usort($stations, fn($a, $b) => $a['order'] <=> $b['order']); usort($stations, fn($a, $b) => $a['order'] <=> $b['order']);
return $stations; return $stations;
} }
@@ -121,15 +93,14 @@ class JsonExportPlugin extends Plugin
$id = $header->station_id ?? $page->slug(); $id = $header->station_id ?? $page->slug();
if ($id === $stationId) { if ($id === $stationId) {
return $this->formatStationForLang($page, $lang); return $this->formatStation($page, $lang);
} }
} }
} }
return null; return null;
} }
private function formatStationForLang($page, $lang) private function formatStation($page, $lang)
{ {
$header = $page->header(); $header = $page->header();
@@ -137,204 +108,155 @@ class JsonExportPlugin extends Plugin
'id' => $header->station_id ?? $page->slug(), 'id' => $header->station_id ?? $page->slug(),
'type' => $header->station_type ?? 'exhibit', 'type' => $header->station_type ?? 'exhibit',
'language' => $lang, 'language' => $lang,
'title' => $this->getTranslatedField($header->title_translations ?? [], $lang), 'title' => $this->getTranslated($header->title_translations ?? [], $lang),
'description' => $this->getTranslatedField($header->description_translations ?? [], $lang), 'description' => $this->getTranslated($header->description_translations ?? [], $lang),
'components' => [] 'components' => []
]; ];
// Process components
if (isset($header->components) && is_array($header->components)) { if (isset($header->components) && is_array($header->components)) {
foreach ($header->components as $component) { foreach ($header->components as $comp) {
$station['components'][] = $this->formatComponent($component, $lang); $station['components'][] = $this->formatComponent($comp, $lang);
} }
} }
return $station; return $station;
} }
private function formatComponent($component, $lang) private function formatComponent($comp, $lang)
{ {
$formatted = [ $out = [
'id' => $component['id'] ?? uniqid(), 'id' => $comp['id'] ?? uniqid(),
'type' => $component['type'] ?? 'text' 'type' => $comp['type'] ?? 'text'
]; ];
switch ($component['type']) { switch ($comp['type']) {
case 'heading': case 'heading':
$formatted['level'] = $component['level'] ?? 'h2'; $out['level'] = $comp['level'] ?? 'h2';
$formatted['content'] = $this->getTranslatedField($component['content'] ?? [], $lang); $out['content'] = $this->getTranslated($comp['content'] ?? [], $lang);
break; break;
case 'text': case 'text':
$formatted['content'] = $this->getTranslatedField($component['content'] ?? [], $lang); $out['content'] = $this->getTranslated($comp['content'] ?? [], $lang);
break; break;
case 'video': case 'video':
$formatted['src'] = $this->getTranslatedField($component['src'] ?? [], $lang); $out['src'] = $this->getTranslated($comp['src'] ?? [], $lang);
$formatted['poster'] = $component['poster'] ?? null; $out['poster'] = $comp['poster'] ?? null;
$formatted['caption'] = $this->getTranslatedField($component['caption'] ?? [], $lang); $out['caption'] = $this->getTranslated($comp['caption'] ?? [], $lang);
break; break;
case 'audio': case 'audio':
$formatted['src'] = $this->getTranslatedField($component['src'] ?? [], $lang); $out['src'] = $this->getTranslated($comp['src'] ?? [], $lang);
$formatted['caption'] = $this->getTranslatedField($component['caption'] ?? [], $lang); $out['caption'] = $this->getTranslated($comp['caption'] ?? [], $lang);
break; break;
case 'tabs': case 'tabs':
$formatted['tabs'] = []; $out['tabs'] = [];
if (isset($component['tabs']) && is_array($component['tabs'])) { foreach ($comp['tabs'] ?? [] as $tab) {
foreach ($component['tabs'] as $tab) { $out['tabs'][] = [
$formatted['tabs'][] = [ 'title' => $this->getTranslated($tab['title'] ?? [], $lang),
'title' => $this->getTranslatedField($tab['title'] ?? [], $lang), 'content' => $this->getTranslated($tab['content'] ?? [], $lang)
'content' => $this->getTranslatedField($tab['content'] ?? [], $lang) ];
];
}
} }
break; break;
case 'slideshow': case 'slideshow':
$formatted['slides'] = []; $out['slides'] = [];
if (isset($component['slides']) && is_array($component['slides'])) { foreach ($comp['slides'] ?? [] as $slide) {
foreach ($component['slides'] as $slide) { $out['slides'][] = [
$formatted['slides'][] = [ 'image' => $slide['image'] ?? '',
'image' => $slide['image'] ?? '', 'caption' => $this->getTranslated($slide['caption'] ?? [], $lang)
'caption' => $this->getTranslatedField($slide['caption'] ?? [], $lang) ];
];
}
} }
break; break;
} }
return $formatted; return $out;
} }
private function getTranslatedField($field, $lang, $fallback = 'en') private function getTranslated($field, $lang)
{ {
if (is_string($field)) { if (is_string($field)) return $field;
return $field; return $field[$lang] ?? $field['en'] ?? reset($field) ?: '';
}
if (is_array($field)) {
return $field[$lang] ?? $field[$fallback] ?? reset($field) ?: '';
}
return '';
} }
private function getTranslations($lang) private function getTranslations($lang)
{ {
// UI translations - these should be stored in config or separate files $t = [
$translations = [
'en' => [ 'en' => [
'appTitle' => 'EU Media Guide', 'appTitle' => 'EU Media Guide',
'scanQR' => 'Scan QR', 'scanQR' => 'Scan QR Code',
'selectLanguage' => 'Select Language', 'selectLanguage' => 'Select Language',
'contentLibrary' => 'Content Library', 'contentLibrary' => 'Content Library',
'noContent' => 'Scan a QR code or select content from the menu', 'loading' => 'Loading...',
'errorLoading' => 'Failed to load content. Please try again.', 'play' => 'Play',
'playAudio' => 'Play Audio', 'pause' => 'Pause',
'pauseAudio' => 'Pause Audio',
'next' => 'Next', 'next' => 'Next',
'previous' => 'Previous', 'previous' => 'Previous',
'closeMenu' => 'Close Menu', 'menu' => 'Menu',
'openMenu' => 'Open Menu', 'close' => 'Close'
'loading' => 'Loading...'
], ],
'fr' => [ 'fr' => [
'appTitle' => 'Guide Média UE', 'appTitle' => 'Guide Média UE',
'scanQR' => 'Scanner QR', 'scanQR' => 'Scanner le code QR',
'selectLanguage' => 'Choisir la langue', 'selectLanguage' => 'Choisir la langue',
'contentLibrary' => 'Bibliothèque de contenu', 'contentLibrary' => 'Bibliothèque',
'noContent' => 'Scannez un code QR ou sélectionnez du contenu dans le menu', 'loading' => 'Chargement...',
'errorLoading' => 'Échec du chargement. Veuillez réessayer.', 'play' => 'Lecture',
'playAudio' => 'Lire l\'audio', 'pause' => 'Pause',
'pauseAudio' => 'Pause audio',
'next' => 'Suivant', 'next' => 'Suivant',
'previous' => 'Précédent', 'previous' => 'Précédent',
'closeMenu' => 'Fermer le menu', 'menu' => 'Menu',
'openMenu' => 'Ouvrir le menu', 'close' => 'Fermer'
'loading' => 'Chargement...'
], ],
'de' => [ 'de' => [
'appTitle' => 'EU Media Guide', 'appTitle' => 'EU Medienführer',
'scanQR' => 'QR scannen', 'scanQR' => 'QR-Code scannen',
'selectLanguage' => 'Sprache wählen', 'selectLanguage' => 'Sprache wählen',
'contentLibrary' => 'Inhaltsbibliothek', 'contentLibrary' => 'Inhaltsbibliothek',
'noContent' => 'Scannen Sie einen QR-Code oder wählen Sie Inhalte aus dem Menü', 'loading' => 'Laden...',
'errorLoading' => 'Laden fehlgeschlagen. Bitte versuchen Sie es erneut.', 'play' => 'Abspielen',
'playAudio' => 'Audio abspielen', 'pause' => 'Pause',
'pauseAudio' => 'Audio pausieren',
'next' => 'Weiter', 'next' => 'Weiter',
'previous' => 'Zurück', 'previous' => 'Zurück',
'closeMenu' => 'Menü schließen', 'menu' => 'Menü',
'openMenu' => 'Menü öffnen', 'close' => 'Schließen'
'loading' => 'Laden...'
], ],
'nl' => [ 'nl' => [
'appTitle' => 'EU Media Gids', 'appTitle' => 'EU Mediagids',
'scanQR' => 'QR scannen', 'scanQR' => 'QR-code scannen',
'selectLanguage' => 'Taal selecteren', 'selectLanguage' => 'Taal selecteren',
'contentLibrary' => 'Inhoudsbibliotheek', 'contentLibrary' => 'Inhoudsbibliotheek',
'noContent' => 'Scan een QR-code of selecteer inhoud uit het menu', 'loading' => 'Laden...',
'errorLoading' => 'Laden mislukt. Probeer het opnieuw.', 'play' => 'Afspelen',
'playAudio' => 'Audio afspelen', 'pause' => 'Pauze',
'pauseAudio' => 'Audio pauzeren',
'next' => 'Volgende', 'next' => 'Volgende',
'previous' => 'Vorige', 'previous' => 'Vorige',
'closeMenu' => 'Menu sluiten', 'menu' => 'Menu',
'openMenu' => 'Menu openen', 'close' => 'Sluiten'
'loading' => 'Laden...' ],
'it' => [
'appTitle' => 'Guida Media UE',
'scanQR' => 'Scansiona codice QR',
'selectLanguage' => 'Seleziona lingua',
'contentLibrary' => 'Libreria contenuti',
'loading' => 'Caricamento...',
'play' => 'Riproduci',
'pause' => 'Pausa',
'next' => 'Successivo',
'previous' => 'Precedente',
'menu' => 'Menu',
'close' => 'Chiudi'
],
'es' => [
'appTitle' => 'Guía de Medios UE',
'scanQR' => 'Escanear código QR',
'selectLanguage' => 'Seleccionar idioma',
'contentLibrary' => 'Biblioteca de contenido',
'loading' => 'Cargando...',
'play' => 'Reproducir',
'pause' => 'Pausa',
'next' => 'Siguiente',
'previous' => 'Anterior',
'menu' => 'Menú',
'close' => 'Cerrar'
] ]
// Add more languages as needed
];
return $translations[$lang] ?? $translations['en'];
}
private function exportAllToJson()
{
$exportPath = $this->grav['locator']->findResource('user://') . '/export/';
if (!is_dir($exportPath)) {
mkdir($exportPath, 0755, true);
}
$languages = $this->grav['config']->get('system.languages.supported', ['en']);
$exported = [];
foreach ($languages as $lang) {
// Export stations
$stations = $this->getStationsList($lang);
file_put_contents(
$exportPath . "stations.{$lang}.json",
json_encode($stations, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
);
// Export each station content
foreach ($stations as $station) {
$content = $this->getStation($station['id'], $lang);
if ($content) {
file_put_contents(
$exportPath . "{$station['id']}.{$lang}.json",
json_encode($content, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
);
}
}
// Export translations
$translations = $this->getTranslations($lang);
file_put_contents(
$exportPath . "translations.{$lang}.json",
json_encode($translations, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
);
$exported[] = $lang;
}
return [
'success' => true,
'exported_languages' => $exported,
'path' => $exportPath
]; ];
return $t[$lang] ?? $t['en'];
} }
} }

View File

@@ -4,9 +4,6 @@ description: Exports Grav content to JSON format for the React media guide app
icon: file-code icon: file-code
author: author:
name: Media Guide Team name: Media Guide Team
homepage: https://github.com/your-org/mediaguide
keywords: json, export, api, multilingual
bugs: https://github.com/your-org/mediaguide/issues
license: MIT license: MIT
form: form: