Skip to content

Commit eb3b5fa

Browse files
using local translations from presets.yaml for server
1 parent 9fda4ab commit eb3b5fa

8 files changed

Lines changed: 78 additions & 74 deletions

File tree

dist/server/public/app.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ function renderPresets() {
110110
input.type = 'radio'
111111
input.name = 'preset'
112112
input.value = preset.id
113-
input.dataset.descriptionKey = `presets.${preset.id}.description`
113+
input.dataset.presetDescription = JSON.stringify(preset.description)
114114
input.dataset.presetOptions = JSON.stringify(preset.options)
115115
if (index === 0) input.checked = true
116116

@@ -667,6 +667,18 @@ function initializeFormatDescription() {
667667
})
668668
})
669669
}
670+
// Pick the right language from a preset description object {de: '...', en: '...'}
671+
function getPresetDescription(descriptionData) {
672+
if (!descriptionData) return ''
673+
try {
674+
const desc = JSON.parse(descriptionData)
675+
const lang = window.i18n ? window.i18n.currentLanguage : 'en'
676+
return desc[lang] || desc['en'] || ''
677+
} catch {
678+
return ''
679+
}
680+
}
681+
670682
// Preset description display
671683
function initializePresetDescription() {
672684
// Use event delegation since presets are loaded dynamically
@@ -675,9 +687,9 @@ function initializePresetDescription() {
675687
const descriptionBox = document.getElementById('preset-description')
676688
const descriptionText = descriptionBox.querySelector('p')
677689

678-
// Get translation key and translate
679-
const descriptionKey = e.target.dataset.descriptionKey
680-
const description = window.i18n ? window.i18n.t(descriptionKey) : ''
690+
const description = getPresetDescription(
691+
e.target.dataset.presetDescription,
692+
)
681693

682694
if (description) {
683695
descriptionText.innerHTML = description
@@ -694,8 +706,9 @@ function initializePresetDescription() {
694706
if (checkedPreset) {
695707
const descriptionBox = document.getElementById('preset-description')
696708
const descriptionText = descriptionBox.querySelector('p')
697-
const descriptionKey = checkedPreset.dataset.descriptionKey
698-
const description = window.i18n ? window.i18n.t(descriptionKey) : ''
709+
const description = getPresetDescription(
710+
checkedPreset.dataset.presetDescription,
711+
)
699712

700713
if (description) {
701714
descriptionText.innerHTML = description

dist/server/public/i18n.js

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,42 +43,43 @@ class I18n {
4343
}
4444

4545
t(key, fallback = '') {
46-
const translation = this.translations[this.currentLanguage]?.[key] ||
47-
this.translations[this.fallbackLanguage]?.[key] ||
48-
fallback ||
49-
key
46+
const translation =
47+
this.translations[this.currentLanguage]?.[key] ||
48+
this.translations[this.fallbackLanguage]?.[key] ||
49+
fallback ||
50+
key
5051
return translation
5152
}
5253

5354
// Update all elements with data-i18n attribute
5455
updatePageTranslations() {
55-
document.querySelectorAll('[data-i18n]').forEach(element => {
56+
document.querySelectorAll('[data-i18n]').forEach((element) => {
5657
const key = element.getAttribute('data-i18n')
5758
const translation = this.t(key)
58-
59+
5960
if (translation.includes('<') && translation.includes('>')) {
6061
element.innerHTML = translation
6162
} else {
6263
element.textContent = translation
6364
}
6465
})
6566

66-
document.querySelectorAll('[data-i18n-placeholder]').forEach(element => {
67+
document.querySelectorAll('[data-i18n-placeholder]').forEach((element) => {
6768
const key = element.getAttribute('data-i18n-placeholder')
6869
element.placeholder = this.t(key)
6970
})
7071

71-
document.querySelectorAll('[data-i18n-title]').forEach(element => {
72+
document.querySelectorAll('[data-i18n-title]').forEach((element) => {
7273
const key = element.getAttribute('data-i18n-title')
7374
element.title = this.t(key)
7475
})
7576

76-
document.querySelectorAll('[data-i18n-html]').forEach(element => {
77+
document.querySelectorAll('[data-i18n-html]').forEach((element) => {
7778
const key = element.getAttribute('data-i18n-html')
7879
element.innerHTML = this.t(key)
7980
})
8081

81-
document.querySelectorAll('[data-i18n-description]').forEach(element => {
82+
document.querySelectorAll('[data-i18n-description]').forEach((element) => {
8283
const key = element.getAttribute('data-i18n-description')
8384
const translation = this.t(key)
8485
element.setAttribute('data-description', translation)
@@ -88,12 +89,13 @@ class I18n {
8889
updateDynamicContent() {
8990
// Update preset description if one is currently selected
9091
const checkedPreset = document.querySelector('input[name="preset"]:checked')
91-
if (checkedPreset && checkedPreset.dataset.descriptionKey) {
92+
if (checkedPreset && checkedPreset.dataset.presetDescription) {
9293
const descriptionBox = document.getElementById('preset-description')
9394
if (descriptionBox && descriptionBox.style.display !== 'none') {
9495
const descriptionText = descriptionBox.querySelector('p')
9596
if (descriptionText) {
96-
const description = this.t(checkedPreset.dataset.descriptionKey)
97+
const desc = JSON.parse(checkedPreset.dataset.presetDescription)
98+
const description = desc[this.currentLanguage] || desc['en'] || ''
9799
descriptionText.innerHTML = description
98100
}
99101
}
@@ -116,7 +118,7 @@ class I18n {
116118
const removeButtons = document.querySelectorAll('.remove-file')
117119
if (removeButtons.length > 0) {
118120
const removeTitle = this.t('files.remove')
119-
removeButtons.forEach(btn => {
121+
removeButtons.forEach((btn) => {
120122
btn.title = removeTitle
121123
})
122124
}
@@ -131,16 +133,16 @@ class I18n {
131133

132134
async init() {
133135
const savedLang = localStorage.getItem('language')
134-
136+
135137
let initialLang = savedLang || navigator.language.split('-')[0] || 'en'
136-
138+
137139
const supportedLanguages = ['en', 'de']
138140
if (!supportedLanguages.includes(initialLang)) {
139141
initialLang = 'en'
140142
}
141143

142144
await this.loadLanguage(this.fallbackLanguage)
143-
145+
144146
if (initialLang !== this.fallbackLanguage) {
145147
await this.loadLanguage(initialLang)
146148
}

dist/server/public/locales/de.json

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,5 @@
243243
"status.autoRefresh": "Seite wird automatisch aktualisiert",
244244
"status.refreshNow": "Jetzt aktualisieren",
245245
"status.error": "Fehler",
246-
"status.errorMessage": "Fehler beim Laden des Status",
247-
"presets.moodle.description": "Moodle ist das weltweit am häufigsten verwendete Open-Source-Lernmanagementsystem. Diese Konfiguration verwendet SCORM 1.2 für maximale Kompatibilität mit Moodle 4.x. Für Moodle 3.x empfehlen wir stattdessen den iframe-Modus. <a href='https://moodle.org' target='_blank'>Mehr erfahren</a>",
248-
"presets.ilias.description": "ILIAS ist ein leistungsstarkes Open-Source-LMS aus Deutschland. Diese Konfiguration nutzt SCORM 1.2 für bestmögliche Kompatibilität mit ILIAS-Versionen. <a href='https://www.ilias.de' target='_blank'>Mehr erfahren</a>",
249-
"presets.opal.description": "OPAL (Online-Plattform für Akademisches Lehren und Lernen) ist das zentrale LMS für sächsische Hochschulen. Optimiert für SCORM 2004. <a href='https://bildungsportal.sachsen.de/opal' target='_blank'>Mehr erfahren</a>",
250-
"presets.generic.description": "Universelle SCORM 2004 Konfiguration für beliebige Lernmanagementsysteme, die den SCORM 2004 Standard unterstützen. Funktioniert mit den meisten modernen LMS-Plattformen.",
251-
"presets.openolat.description": "OpenOlat ist eine Open-Source E-Learning-Plattform aus der Schweiz. Diese Konfiguration nutzt SCORM 1.2 für volle Funktionalität. <a href='https://www.openolat.com' target='_blank'>Mehr erfahren</a>",
252-
"presets.openedx.description": "Open edX ist die Open-Source-Plattform hinter edX.org und wird weltweit für MOOCs eingesetzt. Verwendet SCORM 2004 über das SCORM XBlock. <a href='https://openedx.org' target='_blank'>Mehr erfahren</a>"
246+
"status.errorMessage": "Fehler beim Laden des Status"
253247
}

dist/server/public/locales/en.json

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -243,12 +243,5 @@
243243
"status.autoRefresh": "Page will refresh automatically",
244244
"status.refreshNow": "Refresh Now",
245245
"status.error": "Error",
246-
"status.errorMessage": "Error loading status",
247-
"presets.moodle3.description": "Moodle is the world's most widely used open-source learning management system. This configuration uses SCORM 1.2 for maximum compatibility with Moodle 3.x. <a href='https://moodle.org' target='_blank'>Learn more</a>",
248-
"presets.moodle4.description": "Moodle is the world's most widely used open-source learning management system. This configuration uses SCORM 1.2 for maximum compatibility with Moodle 4.x. For Moodle 3.x we recommend using the iframe mode. <a href='https://moodle.org' target='_blank'>Learn more</a>",
249-
"presets.ilias.description": "XXXILIAS is a powerful open-source LMS from Germany. This configuration uses SCORM 1.2 for best compatibility with ILIAS versions. <a href='https://www.ilias.de' target='_blank'>Learn more</a>",
250-
"presets.opal.description": "OPAL (Online Platform for Academic Teaching and Learning) is the central LMS for Saxon universities. Optimized for SCORM 2004. <a href='https://bildungsportal.sachsen.de/opal' target='_blank'>Learn more</a>",
251-
"presets.generic.description": "Universal SCORM 2004 configuration for any learning management systems that support the SCORM 2004 standard. Works with most modern LMS platforms.",
252-
"presets.openolat.description": "OpenOlat is an open-source e-learning platform from Switzerland. This configuration uses SCORM 1.2 for full functionality. <a href='https://www.openolat.com' target='_blank'>Learn more</a>",
253-
"presets.openedx.description": "Open edX is the open-source platform behind edX.org and is used worldwide for MOOCs. Uses SCORM 2004 via the SCORM XBlock. <a href='https://openedx.org' target='_blank'>Learn more</a>"
246+
"status.errorMessage": "Error loading status"
254247
}

src/server/public/app.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ function renderPresets() {
110110
input.type = 'radio'
111111
input.name = 'preset'
112112
input.value = preset.id
113-
input.dataset.descriptionKey = `presets.${preset.id}.description`
113+
input.dataset.presetDescription = JSON.stringify(preset.description)
114114
input.dataset.presetOptions = JSON.stringify(preset.options)
115115
if (index === 0) input.checked = true
116116

@@ -667,6 +667,18 @@ function initializeFormatDescription() {
667667
})
668668
})
669669
}
670+
// Pick the right language from a preset description object {de: '...', en: '...'}
671+
function getPresetDescription(descriptionData) {
672+
if (!descriptionData) return ''
673+
try {
674+
const desc = JSON.parse(descriptionData)
675+
const lang = window.i18n ? window.i18n.currentLanguage : 'en'
676+
return desc[lang] || desc['en'] || ''
677+
} catch {
678+
return ''
679+
}
680+
}
681+
670682
// Preset description display
671683
function initializePresetDescription() {
672684
// Use event delegation since presets are loaded dynamically
@@ -675,9 +687,9 @@ function initializePresetDescription() {
675687
const descriptionBox = document.getElementById('preset-description')
676688
const descriptionText = descriptionBox.querySelector('p')
677689

678-
// Get translation key and translate
679-
const descriptionKey = e.target.dataset.descriptionKey
680-
const description = window.i18n ? window.i18n.t(descriptionKey) : ''
690+
const description = getPresetDescription(
691+
e.target.dataset.presetDescription,
692+
)
681693

682694
if (description) {
683695
descriptionText.innerHTML = description
@@ -694,8 +706,9 @@ function initializePresetDescription() {
694706
if (checkedPreset) {
695707
const descriptionBox = document.getElementById('preset-description')
696708
const descriptionText = descriptionBox.querySelector('p')
697-
const descriptionKey = checkedPreset.dataset.descriptionKey
698-
const description = window.i18n ? window.i18n.t(descriptionKey) : ''
709+
const description = getPresetDescription(
710+
checkedPreset.dataset.presetDescription,
711+
)
699712

700713
if (description) {
701714
descriptionText.innerHTML = description

src/server/public/i18n.js

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,42 +43,43 @@ class I18n {
4343
}
4444

4545
t(key, fallback = '') {
46-
const translation = this.translations[this.currentLanguage]?.[key] ||
47-
this.translations[this.fallbackLanguage]?.[key] ||
48-
fallback ||
49-
key
46+
const translation =
47+
this.translations[this.currentLanguage]?.[key] ||
48+
this.translations[this.fallbackLanguage]?.[key] ||
49+
fallback ||
50+
key
5051
return translation
5152
}
5253

5354
// Update all elements with data-i18n attribute
5455
updatePageTranslations() {
55-
document.querySelectorAll('[data-i18n]').forEach(element => {
56+
document.querySelectorAll('[data-i18n]').forEach((element) => {
5657
const key = element.getAttribute('data-i18n')
5758
const translation = this.t(key)
58-
59+
5960
if (translation.includes('<') && translation.includes('>')) {
6061
element.innerHTML = translation
6162
} else {
6263
element.textContent = translation
6364
}
6465
})
6566

66-
document.querySelectorAll('[data-i18n-placeholder]').forEach(element => {
67+
document.querySelectorAll('[data-i18n-placeholder]').forEach((element) => {
6768
const key = element.getAttribute('data-i18n-placeholder')
6869
element.placeholder = this.t(key)
6970
})
7071

71-
document.querySelectorAll('[data-i18n-title]').forEach(element => {
72+
document.querySelectorAll('[data-i18n-title]').forEach((element) => {
7273
const key = element.getAttribute('data-i18n-title')
7374
element.title = this.t(key)
7475
})
7576

76-
document.querySelectorAll('[data-i18n-html]').forEach(element => {
77+
document.querySelectorAll('[data-i18n-html]').forEach((element) => {
7778
const key = element.getAttribute('data-i18n-html')
7879
element.innerHTML = this.t(key)
7980
})
8081

81-
document.querySelectorAll('[data-i18n-description]').forEach(element => {
82+
document.querySelectorAll('[data-i18n-description]').forEach((element) => {
8283
const key = element.getAttribute('data-i18n-description')
8384
const translation = this.t(key)
8485
element.setAttribute('data-description', translation)
@@ -88,12 +89,13 @@ class I18n {
8889
updateDynamicContent() {
8990
// Update preset description if one is currently selected
9091
const checkedPreset = document.querySelector('input[name="preset"]:checked')
91-
if (checkedPreset && checkedPreset.dataset.descriptionKey) {
92+
if (checkedPreset && checkedPreset.dataset.presetDescription) {
9293
const descriptionBox = document.getElementById('preset-description')
9394
if (descriptionBox && descriptionBox.style.display !== 'none') {
9495
const descriptionText = descriptionBox.querySelector('p')
9596
if (descriptionText) {
96-
const description = this.t(checkedPreset.dataset.descriptionKey)
97+
const desc = JSON.parse(checkedPreset.dataset.presetDescription)
98+
const description = desc[this.currentLanguage] || desc['en'] || ''
9799
descriptionText.innerHTML = description
98100
}
99101
}
@@ -116,7 +118,7 @@ class I18n {
116118
const removeButtons = document.querySelectorAll('.remove-file')
117119
if (removeButtons.length > 0) {
118120
const removeTitle = this.t('files.remove')
119-
removeButtons.forEach(btn => {
121+
removeButtons.forEach((btn) => {
120122
btn.title = removeTitle
121123
})
122124
}
@@ -131,16 +133,16 @@ class I18n {
131133

132134
async init() {
133135
const savedLang = localStorage.getItem('language')
134-
136+
135137
let initialLang = savedLang || navigator.language.split('-')[0] || 'en'
136-
138+
137139
const supportedLanguages = ['en', 'de']
138140
if (!supportedLanguages.includes(initialLang)) {
139141
initialLang = 'en'
140142
}
141143

142144
await this.loadLanguage(this.fallbackLanguage)
143-
145+
144146
if (initialLang !== this.fallbackLanguage) {
145147
await this.loadLanguage(initialLang)
146148
}

src/server/public/locales/de.json

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,5 @@
243243
"status.autoRefresh": "Seite wird automatisch aktualisiert",
244244
"status.refreshNow": "Jetzt aktualisieren",
245245
"status.error": "Fehler",
246-
"status.errorMessage": "Fehler beim Laden des Status",
247-
"presets.moodle.description": "Moodle ist das weltweit am häufigsten verwendete Open-Source-Lernmanagementsystem. Diese Konfiguration verwendet SCORM 1.2 für maximale Kompatibilität mit Moodle 4.x. Für Moodle 3.x empfehlen wir stattdessen den iframe-Modus. <a href='https://moodle.org' target='_blank'>Mehr erfahren</a>",
248-
"presets.ilias.description": "ILIAS ist ein leistungsstarkes Open-Source-LMS aus Deutschland. Diese Konfiguration nutzt SCORM 1.2 für bestmögliche Kompatibilität mit ILIAS-Versionen. <a href='https://www.ilias.de' target='_blank'>Mehr erfahren</a>",
249-
"presets.opal.description": "OPAL (Online-Plattform für Akademisches Lehren und Lernen) ist das zentrale LMS für sächsische Hochschulen. Optimiert für SCORM 2004. <a href='https://bildungsportal.sachsen.de/opal' target='_blank'>Mehr erfahren</a>",
250-
"presets.generic.description": "Universelle SCORM 2004 Konfiguration für beliebige Lernmanagementsysteme, die den SCORM 2004 Standard unterstützen. Funktioniert mit den meisten modernen LMS-Plattformen.",
251-
"presets.openolat.description": "OpenOlat ist eine Open-Source E-Learning-Plattform aus der Schweiz. Diese Konfiguration nutzt SCORM 1.2 für volle Funktionalität. <a href='https://www.openolat.com' target='_blank'>Mehr erfahren</a>",
252-
"presets.openedx.description": "Open edX ist die Open-Source-Plattform hinter edX.org und wird weltweit für MOOCs eingesetzt. Verwendet SCORM 2004 über das SCORM XBlock. <a href='https://openedx.org' target='_blank'>Mehr erfahren</a>"
246+
"status.errorMessage": "Fehler beim Laden des Status"
253247
}

src/server/public/locales/en.json

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -243,12 +243,5 @@
243243
"status.autoRefresh": "Page will refresh automatically",
244244
"status.refreshNow": "Refresh Now",
245245
"status.error": "Error",
246-
"status.errorMessage": "Error loading status",
247-
"presets.moodle3.description": "Moodle is the world's most widely used open-source learning management system. This configuration uses SCORM 1.2 for maximum compatibility with Moodle 3.x. <a href='https://moodle.org' target='_blank'>Learn more</a>",
248-
"presets.moodle4.description": "Moodle is the world's most widely used open-source learning management system. This configuration uses SCORM 1.2 for maximum compatibility with Moodle 4.x. For Moodle 3.x we recommend using the iframe mode. <a href='https://moodle.org' target='_blank'>Learn more</a>",
249-
"presets.ilias.description": "XXXILIAS is a powerful open-source LMS from Germany. This configuration uses SCORM 1.2 for best compatibility with ILIAS versions. <a href='https://www.ilias.de' target='_blank'>Learn more</a>",
250-
"presets.opal.description": "OPAL (Online Platform for Academic Teaching and Learning) is the central LMS for Saxon universities. Optimized for SCORM 2004. <a href='https://bildungsportal.sachsen.de/opal' target='_blank'>Learn more</a>",
251-
"presets.generic.description": "Universal SCORM 2004 configuration for any learning management systems that support the SCORM 2004 standard. Works with most modern LMS platforms.",
252-
"presets.openolat.description": "OpenOlat is an open-source e-learning platform from Switzerland. This configuration uses SCORM 1.2 for full functionality. <a href='https://www.openolat.com' target='_blank'>Learn more</a>",
253-
"presets.openedx.description": "Open edX is the open-source platform behind edX.org and is used worldwide for MOOCs. Uses SCORM 2004 via the SCORM XBlock. <a href='https://openedx.org' target='_blank'>Learn more</a>"
246+
"status.errorMessage": "Error loading status"
254247
}

0 commit comments

Comments
 (0)