<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de-x-formal">
	<id>https://glossar.hs-augsburg.de/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kowa</id>
	<title>GlossarWiki - Benutzerbeiträge [de-formal]</title>
	<link rel="self" type="application/atom+xml" href="https://glossar.hs-augsburg.de/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kowa"/>
	<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/Spezial:Beitr%C3%A4ge/Kowa"/>
	<updated>2026-07-17T02:10:39Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Master-Studiengang_Interaktive_Mediensysteme_(SPO_2014)&amp;diff=53283</id>
		<title>Master-Studiengang Interaktive Mediensysteme (SPO 2014)</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Master-Studiengang_Interaktive_Mediensysteme_(SPO_2014)&amp;diff=53283"/>
		<updated>2026-02-15T17:27:27Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Detailinformationen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studiengang&lt;br /&gt;
|stgname=Interaktive Mediensysteme&lt;br /&gt;
|kuerzel=IMS 2014&lt;br /&gt;
|abschluss=Master&lt;br /&gt;
|spo=https://www.hs-augsburg.de/Binaries/Binary46888/SPO-MA-IMS-WS2021.pdf&lt;br /&gt;
|datum=2014/01/28&lt;br /&gt;
|homepage=https://www.hs-augsburg.de/interaktive-medien/ims/&lt;br /&gt;
|fakultaet=FKG, FKI&lt;br /&gt;
|studienbeginn=jedes Semester&lt;br /&gt;
|studienabschnitte=Nein&lt;br /&gt;
}}&lt;br /&gt;
==Studienplan==&lt;br /&gt;
&amp;lt;table style=&amp;quot;width: 53.6em; border-collapse: collapse;&amp;quot; class=&amp;quot;studienplan&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt; &amp;lt;/th&amp;gt;&amp;lt;th style=&amp;quot;border: 1px solid #222; background: #555;&amp;quot; class=&amp;quot;top white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;1. Semester&amp;lt;/strong&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;th style=&amp;quot;border: 1px solid #222; background: #555;&amp;quot; class=&amp;quot;top white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;2. Semester&amp;lt;/strong&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;th style=&amp;quot;border: 1px solid #222; background: #555;&amp;quot; class=&amp;quot;top white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;3. Semester&amp;lt;/strong&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr style=&amp;quot;border: 1px solid #222;&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222;&amp;quot; class=&amp;quot;top bgblau white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Masterprojekt&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222; line-height: 1.6em;&amp;quot; class=&amp;quot;top bgblau white&amp;quot;&amp;gt;[[Modul:IMS_2014:Masterprojekt_Konzeption|Masterprojekt Konzeption]]&amp;lt;br /&amp;gt;4 SWS, 15 CP&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222; line-height: 1.6em;&amp;quot; class=&amp;quot;top bgblau white&amp;quot;&amp;gt;[[Modul:IMS_2014:Masterprojekt_Produktion|Masterprojekt Produktion]]&amp;lt;br /&amp;gt;4 SWS, 15 CP&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222; line-height: 1.6em;&amp;quot; class=&amp;quot;top bgblau white&amp;quot;&amp;gt;[[Modul:IMS_2014:Masterarbeit|Masterarbeit]]&amp;lt;br /&amp;gt;25 CP&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr style=&amp;quot;border: 1px solid #222;&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222;&amp;quot; class=&amp;quot;top bgorange white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Kernfächer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222; line-height: 1.6em;&amp;quot; class=&amp;quot;top bgorange white&amp;quot; colspan=&amp;quot;2&amp;quot;&amp;gt;[[Modul:IMS_2014:Kernf%C3%A4cher|Kernfächer]]&amp;lt;br /&amp;gt;8 SWS, 10 CP&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222; line-height: 1.6em;&amp;quot; class=&amp;quot;top white&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr style=&amp;quot;border: 1px solid #222;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222;&amp;quot; class=&amp;quot;top bgrot white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Workshops und Seminare&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222; line-height: 1.6em;&amp;quot; class=&amp;quot;top bgrot white&amp;quot; colspan=&amp;quot;2&amp;quot;&amp;gt;[[Modul:IMS_2014:Workshops_und_Seminare|Workshops und Seminare]]&amp;lt;br /&amp;gt;8 SWS, 10 CP&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222;line-height: 1.6em;&amp;quot; class=&amp;quot;top white&amp;quot;&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr style=&amp;quot;border: 1px solid #222;&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222;&amp;quot; class=&amp;quot;top bgdgrau white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Allgemeine Begleitfächer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222; line-height: 1.6em;&amp;quot; class=&amp;quot;top bgdgrau white&amp;quot;&amp;gt;[[Lehrveranstaltung:IMS_2014:Unternehmensgründung_und_-führung|Unternehmensgründung&amp;lt;br /&amp;gt; und -führung]]&amp;lt;br /&amp;gt;4 SWS, 5 CP&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222; line-height: 1.6em;&amp;quot; class=&amp;quot;top bgdgrau white&amp;quot;&amp;gt;[[Modul:IMS_2014:Projekttechniken|Projekttechniken]]&amp;lt;br /&amp;gt;4 SWS, 5 CP&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222; line-height: 1.6em;&amp;quot; class=&amp;quot;top bgdgrau white&amp;quot;&amp;gt;[[Modul:IMS_2014:Technik-_und_Wissenschaftsethik|Technik- und&amp;lt;br /&amp;gt; Wissenschaftsethik]]&amp;lt;br /&amp;gt;4 SWS, 5 CP&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td/&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222; background: #555;&amp;quot; class=&amp;quot;top bgdgrau white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;16 SWS&amp;lt;br /&amp;gt; 30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222; background: #555;&amp;quot; class=&amp;quot;top bgdgrau white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;16 SWS&amp;lt;br /&amp;gt; 30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td style=&amp;quot;border: 1px solid #222; background: #555;&amp;quot; class=&amp;quot;top bgdgrau white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;4 SWS&amp;lt;br /&amp;gt; 30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--===Masterprojekt===&lt;br /&gt;
Die beiden Masterprojekte [[Modul:IMS_2014:Masterprojekt_Konzeption|Konzeption]] und [[Modul:IMS_2014:Masterprojekt_Produktion|Produktion]]&lt;br /&gt;
werden in jedem Semester angeboten.&lt;br /&gt;
Sie können zwischen drei Schwerpunktthemen wählen:&lt;br /&gt;
&lt;br /&gt;
* [[Lehrveranstaltung:IMS_2014:Animation_and_Interaction_Research_(Konzeption)|Human-Computer Interaction Design (Konzeption)]] und [[Lehrveranstaltung:IMS_2014:Animation_and_Interaction_Research_(Produktion)|Human-Computer Interaction Design (Produktion)]]&lt;br /&gt;
* [[Lehrveranstaltung:IMS_2014:Mobile_Experience_(Konzeption)|Mobile Experience (Konzeption)]] und [[Lehrveranstaltung:IMS_2014:Mobile_Experience_(Produktion)|Mobile Experience (Produktion)]]&lt;br /&gt;
* [[Lehrveranstaltung:IMS_2014:Game_Development _(Konzeption)|Game Development (Konzeption)]] und [[Lehrveranstaltung:IMS_2014:Game_Development_(Produktion)|Game Development (Produktion)]]&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
===Kernfächer===&lt;br /&gt;
Für das Modul [[Modul:IMS_2014:Kernf%C3%A4cher|Kernfächer]] werden in jedem Semester zwei Lehrveranstaltungen im Umfang von jeweils 5 CP angeboten. &lt;br /&gt;
Sie müssen zwei davon erfolgreich absolvieren.&lt;br /&gt;
Derzeit stehen gibt es folgende vier Kernfächer:&lt;br /&gt;
&lt;br /&gt;
* [[Lehrveranstaltung:IMS_2014:Dramaturgie|Dramaturgie]] (Wintersemester)&lt;br /&gt;
* [[Lehrveranstaltung:IMS_2014:Interaction_Engineering|Interaction Engineering]] (Wintersemester)&lt;br /&gt;
* [[Lehrveranstaltung:IMS_2014:User_Experience|User Experience]] (Sommersemester)&lt;br /&gt;
* [[Lehrveranstaltung:IMS_2014:Web-Technologien|Web-Technologien]] (Sommersemester)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--===Workshops und Seminare===&lt;br /&gt;
Für das Modul [[Modul:IMS_2014:Workshops_und_Seminare|Workshops und Seminare]] werden in jedem Semester mindestens zwei Workshops im Umfang von jeweils 2,5 CP und zwei Seminare im Umfang von jeweils 2,5 oder 5 CP angeboten.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--===Allgemeine Begleitfächer===&lt;br /&gt;
Es gibt drei allgemeine Begleitfächer, die alle erfolgreich absolviert werden müssen.&lt;br /&gt;
&lt;br /&gt;
* [[Modul:IMS_2014:Unternehmensgr%C3%BCndung_und_-f%C3%BChrung|Unternehmensgründung und -führung]] (Wintersemester)&lt;br /&gt;
* [[Modul:IMS_2014:Technik-_und_Wissenschaftsethik|Technik- und Wissenschaftsethik]] (Sommersemester)&lt;br /&gt;
* [[Modul:IMS_2014:Projekttechniken|Projekttechniken]] (jedes Semester)&lt;br /&gt;
&lt;br /&gt;
Das Modul [[Modul:IMS_2014:Projekttechniken|Projekttechniken]] wird nicht nur jedes Semester angeboten, sondern es gibt auch noch zwei Lehrveranstaltungen aus denen Sie auswählen können:&lt;br /&gt;
&lt;br /&gt;
* [[Lehrveranstaltung:IMS_2014:Projekttechniken_1|Projekttechniken 1]] (agiles Projektmanagement; Wintersemester)&lt;br /&gt;
* [[Lehrveranstaltung:IMS_2014:Projekttechniken_2|Projekttechniken 2]] (klassisches Projektmanagement; jedes Semester)&lt;br /&gt;
&lt;br /&gt;
Es handelt sich hier formal allerdings nicht um Wahlpflichtveranstaltungen (beide Lehrveranstaltungen haben dieselbe Prüfungsnummer).&lt;br /&gt;
Das heißt, wenn Sie eine Prüfung nicht bestehen, müssen Sie im nächsten Semester an der [[Wiederholungsprüfung]] teilnehmen.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--==Module==&lt;br /&gt;
{{Studiengang:Module}}&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--==Administration==&lt;br /&gt;
*[[Master-Studiengang Interaktive Mediensysteme (SPO 2014)/ToDo|To-Do-Liste]]&lt;br /&gt;
*[[Master-Studiengang Interaktive Mediensysteme (SPO 2014)/Inaktiv|Inaktive Lehrveranstaltungen]]&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Bachelor-Studiengang_Interaktive_Medien_(SPO_2018),_B._Sc.&amp;diff=53282</id>
		<title>Bachelor-Studiengang Interaktive Medien (SPO 2018), B. Sc.</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Bachelor-Studiengang_Interaktive_Medien_(SPO_2018),_B._Sc.&amp;diff=53282"/>
		<updated>2026-02-15T17:25:57Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Wahlpflichtmodule der Spezialisierungsphase */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studiengang&lt;br /&gt;
|stgname=Interaktive Medien&lt;br /&gt;
|kuerzel=IAM 2018 BSc&lt;br /&gt;
|abschluss=Bachelor&lt;br /&gt;
|datum=2017/12/19&lt;br /&gt;
|spo=https://www.hs-augsburg.de/Binaries/Binary41063/SPO-Interaktive-Medien-WS2018-vom-23.06.2020.pdf&lt;br /&gt;
|homepage=https://www.tha.de/ia/&lt;br /&gt;
|fakultaet=FKG, FKI&lt;br /&gt;
|studienbeginn=Wintersemester&lt;br /&gt;
|studienabschnitte=Ja&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Ein Studium gemäß der SPO 2018 endet entweder mit dem Abschluss „Bachelor of Arts“ ({{BA}}) oder&lt;br /&gt;
mit dem Abschluss „Bachelor of Science“ ({{BSc}}). Der Studienplan der ersten beiden Semester ist unabhängig&lt;br /&gt;
vom angestrebten Abschluss. Im dritten Semester muss man sich für einen Abschluss entscheiden, da sich von diesem&lt;br /&gt;
Semester an die Studienpläne für die beiden Abschlüsse unterscheiden.&lt;br /&gt;
&lt;br /&gt;
==Studienplan SPO 2018, Studienrichtung Informatik ({{BSc}})==&lt;br /&gt;
Den Studienplan für den Studienrichtung Gestaltung ({{BA}}) finden Sie unter &amp;lt;br/&amp;gt;[[Bachelor-Studiengang_Interaktive_Medien_(SPO_2018)#Studienplan_SPO_2018.2C_Studienrichtung_Gestaltung_.28B._A..29|Bachelor-Studiengang Interaktive Medien (SPO 2018), {{BA}}]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Legende:&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;span class=&amp;quot;bgorange  white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;gestalterisch&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;lt;!-- &lt;br /&gt;
--&amp;gt;&amp;lt;span class=&amp;quot;bgblue white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;informationstechnisch&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;lt;!-- &lt;br /&gt;
--&amp;gt;&amp;lt;span class=&amp;quot;bggrey white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;übergreifend&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table class=&amp;quot;studienplan&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td rowspan=&amp;quot;3&amp;quot; class=&amp;quot;noborder&amp;quot;&amp;gt; &amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Grundlagen- und Orientierungs-&amp;lt;br/&amp;gt;phase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Spezialisierungsphase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;3&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Vertiefungsphase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;1&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;2&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;3&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;4&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;5&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;6&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;7&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;6&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Design&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen visueller Gestaltung|Grundlagen&amp;lt;br/&amp;gt;visueller Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen interaktiver Gestaltung|Grundlagen&amp;lt;br/&amp;gt;interakt. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; rowspan=&amp;quot;1&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Sprache|Sprache, 4 CP/4 SWS (AWP)]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modulkatalog:IAM (SPO 2018):Workshops und Seminare|Workshops/Sem.&amp;lt;br/&amp;gt;6 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):UX-Design|UX-Design&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Mensch und Gesellschaft|Mensch und Gesellschaft&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen interaktiver Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Mathematik|Mathematik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mathematik --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mathematik --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- UX-Design --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mensch und Gesellschaft --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen interaktiver Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Sprache --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mathematik --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mathematik --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;8&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Praktische Tätigkeit oder Auslandsstudium|Praktische&amp;lt;br/&amp;gt;Tätigkeit&amp;lt;br/&amp;gt;oder&amp;lt;br/&amp;gt;Auslandsstudium&amp;lt;br/&amp;gt;20 CP]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;8&amp;quot; class=&amp;quot;bggrey white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Teamprojekt|Teamprojekt&amp;lt;br/&amp;gt;20 CP/4+4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;4&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Bachelorarbeit|Bachelorarbeit&amp;lt;br/&amp;gt;10 CP]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen dreidimensionaler Gestaltung|Grundlagen&amp;lt;br/&amp;gt;dreidim. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen audiovisueller Gestaltung|Grundlagen&amp;lt;br/&amp;gt;audiovis. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Mensch und Maschine|Mensch und&amp;lt;br/&amp;gt;Maschine&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Raum und Zeit|Raum  und&amp;lt;br/&amp;gt;Zeit&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 3D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 4D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mensche und Maschine --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Raum und Zeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 3D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 4D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mensche und Maschine --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Raum und Zeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;6&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Infor-&amp;lt;br&amp;gt;matik&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Grundlagen der Informatik|Grundlagen&amp;lt;br/&amp;gt;Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Grundlagen der Webtechnologien|Grundlagen&amp;lt;br/&amp;gt;Webtech.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;&lt;br /&gt;
[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;4&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Bachelorprojekt|Bachelorprojekt&amp;lt;br/&amp;gt;8 CP/2 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Web/Netz--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Web/Netz--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen der Programmierung|Grundlagen&amp;lt;br/&amp;gt;Programmierung&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen der Softwareentwicklung|Grundlagen&amp;lt;br/&amp;gt;Softwareentw.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue  white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue  white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Software-Eng./Programmierung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bggrey  white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Praxisseminar|Praxisseminar&amp;lt;br/&amp;gt;2 CP/2 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Projektdurchführung|Projektdurchführung&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Kunde und Markt|Kunde und Markt&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!--  --&amp;gt;&lt;br /&gt;
&amp;lt;!--  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Kunde und Markt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;210 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;32 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;32 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;28 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;28 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Regeln===&lt;br /&gt;
* Die Prüfungen des ersten Semesters müssen spätestens im zweiten Semester erstmals absolviert werden.&lt;br /&gt;
* An Prüfungen der Vertiefungsphase kann nur teilnehmen, wer sämtliche Prüfungen der Grund- und Orientierungsphase sowie mindestens zwei Gestaltungs- und/oder Informatik-Prüfungen der Spezialisierungsphase bestanden hat.&lt;br /&gt;
* An Workshops und Seminaren kann man ebenso wie an den Sprachprüfungen der Fakultät für Natur- und Geisteswissenschaften in jedem Semester seines Studiums teilnehmen.&lt;br /&gt;
* Das Thema der Bachelorarbeit wird frühestens ausgegeben, wenn sämtlichen Prüfungen der Grund- und Orientierungsphase sowie der Spezialdsierungsphase, das Teamprojekt und die praktische Tätigkeit bzw. das Auslandssemester erfolgreich absolviert wurden. Eine Ausnahme bildes das Spachmodul: Dieses darf auch dpäter belegt werden.&lt;br /&gt;
* Das Thema des Bachelorprojektes wird gemeinsam mit dem Thema der Bachelorarbeit vergeben.&lt;br /&gt;
&lt;br /&gt;
===Wahlpflichtmodule der Spezialisierungsphase===&lt;br /&gt;
Im dritten und vierten Semester spezialisiert man sich in zweierlei Hinsicht. Zum einen wird gewählt,&lt;br /&gt;
welchen Anschluss man anstrebt, und zum anderen wählt man aus den Wahlpflichtkatalogen der &lt;br /&gt;
beiden Semester diejenigen Module, mit denen man sich thematisch vertiefend auseinandersetzen möchte.&lt;br /&gt;
&lt;br /&gt;
Es ist allerdings nicht garantiert, dass jedes Wahlpflichtmodul regelmäßig angeboten wird.&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Bachelor-Studiengang_Interaktive_Medien_(SPO_2018)&amp;diff=53281</id>
		<title>Bachelor-Studiengang Interaktive Medien (SPO 2018)</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Bachelor-Studiengang_Interaktive_Medien_(SPO_2018)&amp;diff=53281"/>
		<updated>2026-02-15T17:22:09Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Wahlpflichtmodule der Spezialisierungsphase */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studiengang&lt;br /&gt;
|stgname=Interaktive Medien&lt;br /&gt;
|kuerzel=IAM (SPO 2018)&lt;br /&gt;
|abschluss=Bachelor&lt;br /&gt;
|datum=2017/12/19&lt;br /&gt;
|spo=https://www.hs-augsburg.de/Binaries/Binary41063/SPO-Interaktive-Medien-WS2018-vom-23.06.2020.pdf&lt;br /&gt;
|homepage=https://www.tha.de/ia/&lt;br /&gt;
|fakultaet=FKG, FKI&lt;br /&gt;
|studienbeginn=Wintersemester&lt;br /&gt;
|studienabschnitte=Ja&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Ein Studium im Studiengang „Interaktive Medien“ endet entweder mit dem Abschluss „Bachelor of Arts“ ({{BA}}) oder&lt;br /&gt;
mit dem Abschluss „Bachelor of Science“ ({{BSc}}). Der Studienplan der ersten beiden Semester ist unabhängig&lt;br /&gt;
vom angestrebten Abschluss. Im dritten Semester muss man sich für einen Abschluss entscheiden, da sich von diesem&lt;br /&gt;
Semester an die Studienpläne für die beiden Abschlüsse unterscheiden.&lt;br /&gt;
&lt;br /&gt;
==Studienplan SPO 2018, Studienrichtung Gestaltung ({{BA}})==&lt;br /&gt;
Den Studienplan für den Studienrichtung Informatik ({{BSc}}) finden Sie unter &amp;lt;br/&amp;gt;[[Bachelor-Studiengang_Interaktive_Medien_(SPO_2018),_B._Sc.#Studienplan_SPO_2018.2C_Studienrichtung_Informatik_.28B._Sc..29|Bachelor-Studiengang Interaktive Medien (SPO 2018), {{BSc}}]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Legende:&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;span class=&amp;quot;bgorange  white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;gestalterisch&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;lt;!-- &lt;br /&gt;
--&amp;gt;&amp;lt;span class=&amp;quot;bgblue white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;informationstechnisch&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;lt;!-- &lt;br /&gt;
--&amp;gt;&amp;lt;span class=&amp;quot;bggrey white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;übergreifend&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table class=&amp;quot;studienplan&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td rowspan=&amp;quot;3&amp;quot; class=&amp;quot;noborder&amp;quot;&amp;gt; &amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Grundlagen- und Orientierungs-&amp;lt;br/&amp;gt;phase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Spezialisierungsphase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;3&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Vertiefungsphase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;1&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;2&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;3&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;4&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;5&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;6&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;7&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;6&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Design&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen visueller Gestaltung|Grundlagen&amp;lt;br/&amp;gt;visueller Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen interaktiver Gestaltung|Grundlagen&amp;lt;br/&amp;gt;interakt. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; rowspan=&amp;quot;1&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Sprache|Sprache, 4 CP/4 SWS (AWP)]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modulkatalog:IAM (SPO 2018):Workshops und Seminare|Workshops/Sem.&amp;lt;br/&amp;gt;6 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):UX-Design|UX-Design&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Mensch und Gesellschaft|Mensch und Gesellschaft&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen interaktiver Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Theorie digitaler Medien|Theorie digitaler Medien&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Theorie --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Theorie --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- UX-Design --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mensch und Gesellschaft --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen interaktiver Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Sprache --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Theorie --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Theorie --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;8&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Praktische Tätigkeit oder Auslandsstudium|Praktische&amp;lt;br/&amp;gt;Tätigkeit&amp;lt;br/&amp;gt;oder&amp;lt;br/&amp;gt;Auslandsstudium&amp;lt;br/&amp;gt;20 CP]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;8&amp;quot; class=&amp;quot;bggrey white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Teamprojekt|Teamprojekt&amp;lt;br/&amp;gt;20 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;4&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Bachelorarbeit|Bachelorarbeit&amp;lt;br/&amp;gt;10 CP]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen dreidimensionaler Gestaltung|Grundlagen&amp;lt;br/&amp;gt;dreidim. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen audiovisueller Gestaltung|Grundlagen&amp;lt;br/&amp;gt;audiovis. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Mensch und Maschine|Mensch und&amp;lt;br/&amp;gt;Maschine&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Mensch und Maschine|Mensch und&amp;lt;br/&amp;gt;Maschine&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 3D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 4D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G-FWP --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G-FWP --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 3D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 4D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;6&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Infor-&amp;lt;br&amp;gt;matik&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Grundlagen der Informatik|Grundlagen&amp;lt;br/&amp;gt;Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Grundlagen der Webtechnologien|Grundlagen&amp;lt;br/&amp;gt;Webtech.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Raum und Zeit|Raum und&amp;lt;br/&amp;gt;Zeit&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Raum und Zeit|Raum und&amp;lt;br/&amp;gt;Zeit&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;4&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Bachelorprojekt|Bachelorprojekt&amp;lt;br/&amp;gt;8 CP/2 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Web/Netz--&amp;gt;&lt;br /&gt;
&amp;lt;!-- G/I-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G/I-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Web/Netz--&amp;gt;&lt;br /&gt;
&amp;lt;!-- G-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G/I-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen der Programmierung|Grundlagen&amp;lt;br/&amp;gt;Programmierung&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen der Softwareentwicklung|Grundlagen&amp;lt;br/&amp;gt;Softwareentw.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue  white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue  white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Software-Eng./Programmierung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- I-FWP --&amp;gt;&lt;br /&gt;
&amp;lt;!-- I-FWP --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bggrey  white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Praxisseminar|Praxisseminar&amp;lt;br/&amp;gt;2 CP/2 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Projektdurchführung|Projektdurchführung&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Kunde und Markt|Kunde und Markt&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!--  --&amp;gt;&lt;br /&gt;
&amp;lt;!--  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Kunde und Markt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;210 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;32 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;32 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;28 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;28 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Regeln===&lt;br /&gt;
* Die Prüfungen des ersten Semesters müssen spätestens im zweiten Semester erstmals absolviert werden.&lt;br /&gt;
* An Prüfungen der Vertiefungsphase kann nur teilnehmen, wer sämtliche Prüfungen der Grund- und Orientierungsphase sowie mindestens zwei Gestaltungs- und/oder Informatik-Prüfungen der Spezialisierungsphase bestanden hat.&lt;br /&gt;
* An Workshops und Seminaren kann man ebenso wie an den Sprachprüfungen der Fakultät für Natur- und Geisteswissenschaften in jedem Semester seines Studiums teilnehmen.&lt;br /&gt;
* Das Thema der Bachelorarbeit wird frühestens ausgegeben, wenn sämtlichen Prüfungen der Grund- und Orientierungsphase sowie der Spezialdsierungsphase, das Teamprojekt und die praktische Tätigkeit bzw. das Auslandssemester erfolgreich absolviert wurden. Eine Ausnahme bildes das Spachmodul: Dieses darf auch dpäter belegt werden.&lt;br /&gt;
* Das Thema des Bachelorprojektes wird gemeinsam mit dem Thema der Bachelorarbeit vergeben.&lt;br /&gt;
&lt;br /&gt;
===Wahlpflichtmodule der Spezialisierungsphase===&lt;br /&gt;
Im dritten und vierten Semester spezialisiert man sich in zweierlei Hinsicht. Zum einen wird gewählt,&lt;br /&gt;
welchen Anschluss man anstrebt, und zum anderen wählt man aus den Wahlpflichtkatalogen der &lt;br /&gt;
beiden Semester diejenigen Module, mit denen man sich thematisch vertiefend auseinandersetzen möchte.&lt;br /&gt;
&lt;br /&gt;
Es ist allerdings nicht garantiert, dass jedes Wahlpflichtmodul regelmäßig angeboten wird.&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Hauptseite/Kategorien&amp;diff=53280</id>
		<title>Hauptseite/Kategorien</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Hauptseite/Kategorien&amp;diff=53280"/>
		<updated>2026-02-15T17:21:01Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Kategorien */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Kategorien==&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%&amp;quot;&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#dddddd; width:50%&amp;quot; align=&amp;quot;center&amp;quot;| &#039;&#039;&#039;Studienpläne&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#dddddd; width:50%&amp;quot; align=&amp;quot;center&amp;quot;| &#039;&#039;&#039;Lehrveranstaltungen&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#fffff1;&amp;quot;  align=&amp;quot;left&amp;quot;| &lt;br /&gt;
* [[Bachelor-Studiengang Interaktive Medien (SPO 2018), {{BA}}]]&lt;br /&gt;
* [[Bachelor-Studiengang Interaktive Medien (SPO 2018), {{BSc}}]]&lt;br /&gt;
* [[Master-Studiengang Interaktive Mediensysteme (SPO 2014)|Master-Studiengang Interaktive Mediensysteme]]&lt;br /&gt;
* [[Bachelor-Studiengang Interaktive Medien (SPO_2006)]] (ausgelaufen)&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#fffff1;&amp;quot;  align=&amp;quot;left&amp;quot;| &lt;br /&gt;
* [[GlossarWiki:Bücher/Datenmanagement/WiSe2025|Datenmanagement]]  (WiSe 2025/2026)&lt;br /&gt;
* [[GlossarWiki:Bücher/Full-Stack-Webentwicklung/SoSe2025|Full-Stack-Webentwicklung]]  (SoSe 2025)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%&amp;quot;&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#dddddd; width:33.3%&amp;quot; align=&amp;quot;center&amp;quot;| &#039;&#039;&#039;Schwerpunktthemen&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#dddddd; width:33.3%&amp;quot; align=&amp;quot;center&amp;quot;| &#039;&#039;&#039;{{HSA}}-Kategorien&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#dddddd; width:33.3%&amp;quot; align=&amp;quot;center&amp;quot;| &#039;&#039;&#039;Klassifizierende Kategorien&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#fffff1;&amp;quot;  align=&amp;quot;left&amp;quot;| &lt;br /&gt;
*[[:Kategorie:Logik|Logik]]&lt;br /&gt;
*[[:Kategorie:Mengenlehre|Mengenlehre]]&lt;br /&gt;
*[[:Kategorie:Stochastik|Stochastik]]&lt;br /&gt;
**[[:Kategorie:Zufallsexperiment|Zufallsexperiment]]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#fffff1;&amp;quot;  align=&amp;quot;left&amp;quot;| &lt;br /&gt;
&#039;&#039;&#039;Allgemein&#039;&#039;&#039;&lt;br /&gt;
*[[:Kategorie: HSA|Hochschule Augsburg]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&#039;&#039;&#039;Fachgebiete&#039;&#039;&#039;&lt;br /&gt;
*[[:Kategorie: Allgemeinwissenschaften|Allgemeinwissenschaften]]&lt;br /&gt;
&amp;lt;!--*[[:Kategorie: Elektrotechnik|Elektrotechnik]]--&amp;gt;&lt;br /&gt;
*[[:Kategorie: Gestaltung|Gestaltung]]&lt;br /&gt;
*[[:Kategorie: Informatik|Informatik]]&lt;br /&gt;
*[[:Kategorie: Mathematik|Mathematik]]&lt;br /&gt;
&amp;lt;!--*[[:Kategorie: Maschinenbau|Maschinenbau]]--&amp;gt;&lt;br /&gt;
*[[:Kategorie: Wirtschaftswissenschaften|Wirtschaftswissenschaften]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&#039;&#039;&#039;Lehrverastaltungen&#039;&#039;&#039;&lt;br /&gt;
*[[:Kategorie:Lehrveranstaltung|Lehrveranstaltungen]]&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#fffff1;&amp;quot;  align=&amp;quot;left&amp;quot;|&lt;br /&gt;
*[[:Kategorie:Begriffsklärung|Begriffsklärungsseiten]]: Für mehrdeutige Begriffe&lt;br /&gt;
*[[:Kategorie:Glossar|Glossar]]: Die eigentlichen Glossar-Beiträge&lt;br /&gt;
*[[:Kategorie:Autor|Autor]]: Publikationslisten von Autoren&lt;br /&gt;
*[[:Kategorie:Person|Person]]: Personenbezogene Artikel&lt;br /&gt;
*[[:Kategorie:Beispiel|Beispiel]]: Beispiele zu einzelnen Definitionen&lt;br /&gt;
*[http://glossar.hs-augsburg.de/beispiel/tutorium/ Tutorien-Sourcen] (SVN-Repositories – vgl. [[Subversion]], [[Subversion/HowTo]])&lt;br /&gt;
*[[:Kategorie:Vergleich|Vergleich]]: Vergleiche von Konzepten, Definitionen etc.&lt;br /&gt;
*[[:Kategorie:Handbuch|Handbuch]]: Klassen-/Methoden-/...-Definitionen und Beschreibungen &lt;br /&gt;
*[[:Kategorie:HowTo|HowTo]]: Tipps und Tricks, kurze Anleitungen&lt;br /&gt;
*[[:Kategorie:Tool|Tool]]: Tools und Werkzeuge aller Art&lt;br /&gt;
*[[:Kategorie:Mathematischer Satz|Mathematischer Satz]]: Mathematische Sätze, Theoreme etc.&lt;br /&gt;
*[[:Kategorie:Deutsche Sprache|Deutsche Sprache]]: Rechtschreibung, Grammatik, Fremdwörter etc.&lt;br /&gt;
*[[:Kategorie:Quelle|Quelle]]: Quellenangaben zu Büchern, Aufsätzen, Internetquellen etc. &lt;br /&gt;
*[[:Kategorie:Jahr|Jahr]]: Die Ereignisse der einzelnen Jahre &lt;br /&gt;
*[[:Kategorie: {{SITENAME}}|Artikel, die {{SITENAME}} selbst betreffen]]&lt;br /&gt;
*[[:Kategorie: Formular|Alle Formulare]] (zur vereinfachten Datenerfassung)&lt;br /&gt;
*[[:Kategorie: Vorlage|Alle Vorlagen]] (= {{SITENAME}}-Templates)&lt;br /&gt;
*[[:Kategorie:Versteckte Kategorien|Versteckte Kategorien]]&lt;br /&gt;
*[[Spezial:Newpages|Neueste Artikel]]&lt;br /&gt;
*[[Spezial:Allpages|Alle Artikel]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Hauptseite/Kategorien&amp;diff=53279</id>
		<title>Hauptseite/Kategorien</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Hauptseite/Kategorien&amp;diff=53279"/>
		<updated>2026-01-20T11:11:31Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Kategorien */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Kategorien==&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%&amp;quot;&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#dddddd; width:50%&amp;quot; align=&amp;quot;center&amp;quot;| &#039;&#039;&#039;Studienpläne&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#dddddd; width:50%&amp;quot; align=&amp;quot;center&amp;quot;| &#039;&#039;&#039;Lehrveranstaltungen&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#fffff1;&amp;quot;  align=&amp;quot;left&amp;quot;| &lt;br /&gt;
* [[Bachelor-Studiengang Interaktive Medien (SPO 2018), {{BA}}]]&lt;br /&gt;
* [[Bachelor-Studiengang Interaktive Medien (SPO 2018), {{BSc}}]]&lt;br /&gt;
* [[Master-Studiengang Interaktive Mediensysteme (SPO 2014)|Master-Studiengang Interaktive Mediensysteme]]&lt;br /&gt;
* [[Bachelor-Studiengang Interaktive Medien (SPO_2006)]] (ausgelaufen)&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#fffff1;&amp;quot;  align=&amp;quot;left&amp;quot;| &lt;br /&gt;
* [[GlossarWiki:Bücher/Datenmanagement/WiSe2025|Datenmanagement]]  (WiSe 2025/2026)&lt;br /&gt;
* [[GlossarWiki:Bücher/Full-Stack-Webentwicklung/SoSe2025|Full-Stack-Webentwicklung]]  (SoSe 2025)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%&amp;quot;&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#dddddd; width:33.3%&amp;quot; align=&amp;quot;center&amp;quot;| &#039;&#039;&#039;Schwerpunktthemen&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#dddddd; width:33.3%&amp;quot; align=&amp;quot;center&amp;quot;| &#039;&#039;&#039;{{HSA}}-Kategorien&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#dddddd; width:33.3%&amp;quot; align=&amp;quot;center&amp;quot;| &#039;&#039;&#039;Klassifizierende Kategorien&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#fffff1;&amp;quot;  align=&amp;quot;left&amp;quot;| &lt;br /&gt;
*[[:Kategorie:Logik|Logik]]&lt;br /&gt;
*[[:Kategorie:Mengenlehre|Mengenlehre]]&lt;br /&gt;
*[[:Kategorie:Stochastik|Stochastik]]&lt;br /&gt;
**[[:Kategorie:Zufallsexperiment|Zufallsexperiment]]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#fffff1;&amp;quot;  align=&amp;quot;left&amp;quot;| &lt;br /&gt;
&#039;&#039;&#039;Allgemein&#039;&#039;&#039;&lt;br /&gt;
*[[:Kategorie: HSA|Hochschule Augsburg]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&#039;&#039;&#039;Fachgebiete&#039;&#039;&#039;&lt;br /&gt;
*[[:Kategorie: Allgemeinwissenschaften|Allgemeinwissenschaften]]&lt;br /&gt;
&amp;lt;!--*[[:Kategorie: Elektrotechnik|Elektrotechnik]]--&amp;gt;&lt;br /&gt;
*[[:Kategorie: Gestaltung|Gestaltung]]&lt;br /&gt;
*[[:Kategorie: Informatik|Informatik]]&lt;br /&gt;
*[[:Kategorie: Mathematik|Mathematik]]&lt;br /&gt;
&amp;lt;!--*[[:Kategorie: Maschinenbau|Maschinenbau]]--&amp;gt;&lt;br /&gt;
*[[:Kategorie: Wirtschaftswissenschaften|Wirtschaftswissenschaften]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&#039;&#039;&#039;Module&#039;&#039;&#039;&lt;br /&gt;
*[[:Kategorie:Modul|Modul]]&lt;br /&gt;
*[[:Kategorie:Lehrveranstaltung|Lehrveranstaltungen]]&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#fffff1;&amp;quot;  align=&amp;quot;left&amp;quot;|&lt;br /&gt;
*[[:Kategorie:Begriffsklärung|Begriffsklärungsseiten]]: Für mehrdeutige Begriffe&lt;br /&gt;
*[[:Kategorie:Glossar|Glossar]]: Die eigentlichen Glossar-Beiträge&lt;br /&gt;
*[[:Kategorie:Autor|Autor]]: Publikationslisten von Autoren&lt;br /&gt;
*[[:Kategorie:Person|Person]]: Personenbezogene Artikel&lt;br /&gt;
*[[:Kategorie:Beispiel|Beispiel]]: Beispiele zu einzelnen Definitionen&lt;br /&gt;
*[http://glossar.hs-augsburg.de/beispiel/tutorium/ Tutorien-Sourcen] (SVN-Repositories – vgl. [[Subversion]], [[Subversion/HowTo]])&lt;br /&gt;
*[[:Kategorie:Vergleich|Vergleich]]: Vergleiche von Konzepten, Definitionen etc.&lt;br /&gt;
*[[:Kategorie:Handbuch|Handbuch]]: Klassen-/Methoden-/...-Definitionen und Beschreibungen &lt;br /&gt;
*[[:Kategorie:HowTo|HowTo]]: Tipps und Tricks, kurze Anleitungen&lt;br /&gt;
*[[:Kategorie:Tool|Tool]]: Tools und Werkzeuge aller Art&lt;br /&gt;
*[[:Kategorie:Mathematischer Satz|Mathematischer Satz]]: Mathematische Sätze, Theoreme etc.&lt;br /&gt;
*[[:Kategorie:Deutsche Sprache|Deutsche Sprache]]: Rechtschreibung, Grammatik, Fremdwörter etc.&lt;br /&gt;
*[[:Kategorie:Quelle|Quelle]]: Quellenangaben zu Büchern, Aufsätzen, Internetquellen etc. &lt;br /&gt;
*[[:Kategorie:Jahr|Jahr]]: Die Ereignisse der einzelnen Jahre &lt;br /&gt;
*[[:Kategorie: {{SITENAME}}|Artikel, die {{SITENAME}} selbst betreffen]]&lt;br /&gt;
*[[:Kategorie: Formular|Alle Formulare]] (zur vereinfachten Datenerfassung)&lt;br /&gt;
*[[:Kategorie: Vorlage|Alle Vorlagen]] (= {{SITENAME}}-Templates)&lt;br /&gt;
*[[:Kategorie:Versteckte Kategorien|Versteckte Kategorien]]&lt;br /&gt;
*[[Spezial:Newpages|Neueste Artikel]]&lt;br /&gt;
*[[Spezial:Allpages|Alle Artikel]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=MediaWiki:Common.css&amp;diff=53278</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=MediaWiki:Common.css&amp;diff=53278"/>
		<updated>2026-01-20T07:59:00Z</updated>

		<summary type="html">&lt;p&gt;Kowa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* CSS an dieser Stelle wirkt sich auf alle Skins aus */&lt;br /&gt;
&lt;br /&gt;
:target::before&lt;br /&gt;
{ content:&amp;quot;&amp;quot;; &lt;br /&gt;
  position:relative; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#catlinks li a&lt;br /&gt;
{ background: #DDD !important; }&lt;br /&gt;
&lt;br /&gt;
a.mw-selflink&lt;br /&gt;
{&lt;br /&gt;
  color: #333;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
a.bgorange:visited, a.bgrot:visited, a.bgblue:visited, a.bgdgrau:visited, a.bggrau:visited, a.new:visited&lt;br /&gt;
{&lt;br /&gt;
  color: white !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
a:visited, a.new:visited, #p-personal a.new:visited, a.external:visited, a code&lt;br /&gt;
{&lt;br /&gt;
  color: #337ab7 !important;&lt;br /&gt;
}&lt;br /&gt;
*/&lt;br /&gt;
a:visited, #p-personal a.new:visited, a.external:visited, a code&lt;br /&gt;
{&lt;br /&gt;
  color: #337ab7 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
h5&lt;br /&gt;
{&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
p&lt;br /&gt;
{&lt;br /&gt;
  max-width: 45em;&lt;br /&gt;
  overflow: hidden&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
p.no-max-width&lt;br /&gt;
{&lt;br /&gt;
  max-width: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
pre, code, tt, .mw-geshi&lt;br /&gt;
{&lt;br /&gt;
  font-family: consolas, &amp;quot;courier new&amp;quot;, courier, monospace;&lt;br /&gt;
  font-size:  100%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.indent&lt;br /&gt;
{&lt;br /&gt;
  display: block;&lt;br /&gt;
  margin:  0 0 0 1em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.table_biblio&lt;br /&gt;
{&lt;br /&gt;
  margin:    0 0 1em 1em; &lt;br /&gt;
  font-size: 95%;&lt;br /&gt;
  max-width: 45em; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.table_biblio td&lt;br /&gt;
{&lt;br /&gt;
  padding: 4;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.table_no_border&lt;br /&gt;
{&lt;br /&gt;
  margin: 1em 1em 1em 0;&lt;br /&gt;
  border: 0;&lt;br /&gt;
  background: white;&lt;br /&gt;
  border-collapse: collapse&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.subtable&lt;br /&gt;
{&lt;br /&gt;
  text-align:left;&lt;br /&gt;
  background-color: #EEEEFF !important;&lt;br /&gt;
  font-weight:bold&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable, &lt;br /&gt;
.datatable-rt, &lt;br /&gt;
.datatable-modules, &lt;br /&gt;
.datatable-sql&lt;br /&gt;
{&lt;br /&gt;
  margin: 1ex 1em 1ex 0;&lt;br /&gt;
  border:1px #aaa solid;&lt;br /&gt;
  background:white;&lt;br /&gt;
  border-collapse:collapse;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable th, .datatable td,&lt;br /&gt;
.datatable-rt th,.datatable-rt td, &lt;br /&gt;
.datatable-modules th,.datatable-modules td,&lt;br /&gt;
.datatable-sql th,.datatable-sql td &lt;br /&gt;
{&lt;br /&gt;
  border:1px #aaa solid;&lt;br /&gt;
  padding:1ex&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable-sql th,.datatable-sql td &lt;br /&gt;
{&lt;br /&gt;
  font-family: consolas, &amp;quot;courier new&amp;quot;, courier, monospace;&lt;br /&gt;
  font-size:  95%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable tbody tr:nth-child(odd)  td, &lt;br /&gt;
.datatable-rt tbody tr:nth-child(odd)  td, &lt;br /&gt;
.datatable-sql tbody tr:nth-child(odd)  td &lt;br /&gt;
{  &lt;br /&gt;
   background-color: #eee; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable-modules tbody tr:nth-child(odd)  td&lt;br /&gt;
{  &lt;br /&gt;
   background-color: #fcfcfc; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable th, &lt;br /&gt;
.datatable-rt th, &lt;br /&gt;
.datatable-modules th, &lt;br /&gt;
.datatable-sql th&lt;br /&gt;
{ &lt;br /&gt;
  vertical-align:top;&lt;br /&gt;
  text-align:left;&lt;br /&gt;
  background-color: #ccc;&lt;br /&gt;
  font-weight:bold&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable td, &lt;br /&gt;
.datatable-rt td, &lt;br /&gt;
.datatable-modules td, &lt;br /&gt;
.datatable-sql td&lt;br /&gt;
{&lt;br /&gt;
  vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.formtable td, .formtable th&lt;br /&gt;
{&lt;br /&gt;
  vertical-align: middle !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 47.9375em) {&lt;br /&gt;
  .datatable-rt {&lt;br /&gt;
    border:0;&lt;br /&gt;
    padding-bottom: 1.4375em;&lt;br /&gt;
    padding-bottom: 0;&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    /*&lt;br /&gt;
     * make everything display block so it &lt;br /&gt;
     * aligns vertically&lt;br /&gt;
     */&lt;br /&gt;
    /* Labeling&lt;br /&gt;
     * adding a data-title attribute to the cells&lt;br /&gt;
     * lets us add text before the content to provide&lt;br /&gt;
     * the missing context&lt;br /&gt;
     * &lt;br /&gt;
     * Markup: &lt;br /&gt;
     *   &amp;lt;td data-title=&amp;quot;Column Header&amp;quot;&amp;gt;Content Here&amp;lt;/td&amp;gt;&lt;br /&gt;
     * &lt;br /&gt;
     * Display:&lt;br /&gt;
     *   Column Header: Content Here&lt;br /&gt;
     */&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt caption {&lt;br /&gt;
    display: block;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt thead, .datatable-rt tbody tr:first-child {&lt;br /&gt;
    display: none;&lt;br /&gt;
    visibility: hidden;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt tbody {&lt;br /&gt;
    border:0;&lt;br /&gt;
    display: block;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    white-space: normal;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt tr, .datatable-rt td {&lt;br /&gt;
    border:1px #aaa solid;&lt;br /&gt;
    display: block;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    white-space: normal;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt tr {&lt;br /&gt;
    margin-bottom: 1.5em;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt th[data-title]:before,&lt;br /&gt;
  table td[data-title]:before {&lt;br /&gt;
    content: attr(data-title) &amp;quot;\a&amp;quot;;&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
    white-space: pre;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt th:not([data-title]) {&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt td:empty {&lt;br /&gt;
    display: none;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.citetable&lt;br /&gt;
{&lt;br /&gt;
  margin: 1em 1em 1em 0;&lt;br /&gt;
  border:1px #aaa solid;&lt;br /&gt;
  background:white;&lt;br /&gt;
  border-collapse:collapse;&lt;br /&gt;
}&lt;br /&gt;
.citetable th,.citetable td&lt;br /&gt;
{&lt;br /&gt;
  border:1px #aaa solid;&lt;br /&gt;
  padding:1ex;&lt;br /&gt;
  max-width: 400px; &lt;br /&gt;
  word-wrap: break-word;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.citetable th&lt;br /&gt;
{&lt;br /&gt;
  text-align:center;&lt;br /&gt;
  background-color: #EEEEFF;&lt;br /&gt;
  font-weight:bold&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.citetable td&lt;br /&gt;
{&lt;br /&gt;
  vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.menutable&lt;br /&gt;
{&lt;br /&gt;
  margin: 0 0 1em 0;&lt;br /&gt;
  border: 0;&lt;br /&gt;
  background: white;&lt;br /&gt;
  border-collapse:collapse&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.menutable p&lt;br /&gt;
{&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
.mw-content-ltr&lt;br /&gt;
{&lt;br /&gt;
    margin-top: 1em;&lt;br /&gt;
    margin-bottom: 1em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-code&lt;br /&gt;
{&lt;br /&gt;
  font-family: consolas, &amp;quot;courier new&amp;quot;, courier, monospace;&lt;br /&gt;
  font-size:  95%;&lt;br /&gt;
  max-width: 57em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
pre&lt;br /&gt;
{&lt;br /&gt;
  max-width: 57em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
code &lt;br /&gt;
{&lt;br /&gt;
    background-color: #FFF;&lt;br /&gt;
    border: 0;&lt;br /&gt;
    border-radius: 0;&lt;br /&gt;
    padding: 0 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
td&lt;br /&gt;
{ &lt;br /&gt;
  padding: 0.2em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#toc ul&lt;br /&gt;
{&lt;br /&gt;
  margin-left: -0.5em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent  ul&lt;br /&gt;
{&lt;br /&gt;
  list-style-type: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent  ul, #bodyContent  ol&lt;br /&gt;
{&lt;br /&gt;
   overflow: hidden;&lt;br /&gt;
   max-width: 43.4em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-body a, .mw-body a:visited, .mw-body a.new:visited&lt;br /&gt;
{&lt;br /&gt;
  color: #337ab7;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-body ul&lt;br /&gt;
{&lt;br /&gt;
  list-style: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-body ul, .mw-body ol&lt;br /&gt;
{&lt;br /&gt;
  padding-left: 0ex !important;&lt;br /&gt;
  padding-bottom: 0ex !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent .inputSpan ul &amp;gt; li::before,  &lt;br /&gt;
#bodyContent .redirectMsg ul &amp;gt; li::before, &lt;br /&gt;
li#mw-imagepage-reupload-link::before,&lt;br /&gt;
li#mw-imagepage-upload-disallowed::before&lt;br /&gt;
{&lt;br /&gt;
  content: &amp;quot;&amp;quot; !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
li#mw-imagepage-reupload-link .plainlinks,&lt;br /&gt;
li#mw-imagepage-upload-disallowed&lt;br /&gt;
{&lt;br /&gt;
  text-indent: -1.6em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent ul &amp;gt; li::before &lt;br /&gt;
{&lt;br /&gt;
  content: &amp;quot;\2013 \0020&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.select2-container-multi .select2-search-choice-close {&lt;br /&gt;
    left: -15px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent  ol &amp;gt; li&lt;br /&gt;
{&lt;br /&gt;
   margin-left: 1.6em;&lt;br /&gt;
}&lt;br /&gt;
#bodyContent  ul &amp;gt; li&lt;br /&gt;
{&lt;br /&gt;
   text-indent: -0.9em;&lt;br /&gt;
   margin-left: 1.6em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent ul &amp;gt; li.gallerybox&lt;br /&gt;
{&lt;br /&gt;
   text-indent: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent .rightfloat  ul &amp;gt; li&lt;br /&gt;
{&lt;br /&gt;
   margin-left: 3.9em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent #mw-subcategories ul &amp;gt; li::before,&lt;br /&gt;
#toc ul &amp;gt; li::before, &lt;br /&gt;
.toc ul &amp;gt; li::before, &lt;br /&gt;
#bodyContent  .ms-list &amp;gt; li::before,&lt;br /&gt;
#bodyContent .gallerybox::before, &lt;br /&gt;
#bodyContent  ul#filetoc &amp;gt; li::before&lt;br /&gt;
{&lt;br /&gt;
  content: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-search-profile-tabs .search-types a&lt;br /&gt;
{&lt;br /&gt;
  display: inline !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-search-result-heading&lt;br /&gt;
{&lt;br /&gt;
   display: inline !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-search-results .searchresult, .mw-search-result-data&lt;br /&gt;
{&lt;br /&gt;
   text-indent: 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
li&lt;br /&gt;
{&lt;br /&gt;
  max-width: 50em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
dd&lt;br /&gt;
{&lt;br /&gt;
  max-width: 50em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.thumbinner&lt;br /&gt;
{&lt;br /&gt;
  margin-bottom: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-headline &lt;br /&gt;
{ &lt;br /&gt;
  padding-top: 0px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#mw-createaccount-cta, #pt-createaccount&lt;br /&gt;
{&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-highlight&lt;br /&gt;
{&lt;br /&gt;
    background-color: #ffffff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_quality&lt;br /&gt;
{&lt;br /&gt;
  margin: 0px 0px 15px 0px;&lt;br /&gt;
  padding: 0.2ex;&lt;br /&gt;
  font-size: 75%;&lt;br /&gt;
  background-color: #DDD;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_quality p&lt;br /&gt;
{&lt;br /&gt;
  padding-left: 1ex;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_quality table&lt;br /&gt;
{&lt;br /&gt;
  margin: 0 1px 1px 0;&lt;br /&gt;
}&lt;br /&gt;
.wiki_quality td&lt;br /&gt;
{&lt;br /&gt;
  border: 1px #DDD solid !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_red&lt;br /&gt;
{&lt;br /&gt;
    background-color: #FFA288 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_yellow&lt;br /&gt;
{&lt;br /&gt;
    background-color: #FFECAD !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_green&lt;br /&gt;
{&lt;br /&gt;
    background-color: #96D1AD !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_warning_01&lt;br /&gt;
{&lt;br /&gt;
   margin: 0px 0px 7px 0px;&lt;br /&gt;
   padding: 1ex;&lt;br /&gt;
   border: 5px solid #FFE489;&lt;br /&gt;
   font-size: 95%&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_warning_02&lt;br /&gt;
{&lt;br /&gt;
   margin: 0px 0px 7px 0px;&lt;br /&gt;
   padding: 1ex;&lt;br /&gt;
   border: 5px solid #FFA288;&lt;br /&gt;
   font-size: 95%&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_highlight_01&lt;br /&gt;
{&lt;br /&gt;
   margin: 0px 0px 7px 0px;&lt;br /&gt;
   padding: 1ex;&lt;br /&gt;
   border: 5px solid #96D1AD;&lt;br /&gt;
   font-size: 95%&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_disambiguation_01&lt;br /&gt;
{&lt;br /&gt;
  border: 1px solid #aaa;&lt;br /&gt;
  border-width:2px 0 2px 0;&lt;br /&gt;
  background-color: #f9f9f9;&lt;br /&gt;
  padding: 5px 5px 0px 5px;&lt;br /&gt;
  margin: 0px 0px 7px 0px;&lt;br /&gt;
  font-size:100%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.inline * {display:inline}&lt;br /&gt;
&lt;br /&gt;
.mw-codeX&lt;br /&gt;
{&lt;br /&gt;
  max-width: 48em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.formInput&lt;br /&gt;
{ &lt;br /&gt;
  width: 30em&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.formula&lt;br /&gt;
{&lt;br /&gt;
  display: block;&lt;br /&gt;
  margin:  0 0 1ex 1em;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.regular&lt;br /&gt;
{&lt;br /&gt;
  font-style: regular;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.quote&lt;br /&gt;
{&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-style: italic;&lt;br /&gt;
  margin:  0 0 1ex 1em;&lt;br /&gt;
  max-width: 43em;&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.quote em, .quote i, i em, em em, em i&lt;br /&gt;
{&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.quote2&lt;br /&gt;
{&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
  margin:  0 0 0 1em;&lt;br /&gt;
  max-width: 44em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
div.floatright&lt;br /&gt;
{&lt;br /&gt;
  clear: right;&lt;br /&gt;
  float: right;&lt;br /&gt;
  margin: 0.5em 0 0.8em 1.4em;&lt;br /&gt;
  position: relative;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.floatleft&lt;br /&gt;
{&lt;br /&gt;
  clear: left;&lt;br /&gt;
  float: left;&lt;br /&gt;
  margin: 0.5em 0 0.8em 1.4em;&lt;br /&gt;
  position: relative;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-code &lt;br /&gt;
{&lt;br /&gt;
  margin:  0 0 1ex 1em;&lt;br /&gt;
}&lt;br /&gt;
/* Add arrows to toggle-blocks for collapsible elements */&lt;br /&gt;
.mw-collapsible-arrowtoggle.mw-collapsible-toggle-expanded {&lt;br /&gt;
 padding-left: 20px !important;&lt;br /&gt;
 background-image: url(&#039;//upload.wikimedia.&lt;br /&gt;
org/wikipedia/commons/1/10/MediaWiki_Vector_skin_action_arrow.png&#039;);&lt;br /&gt;
 background-repeat: no-repeat;&lt;br /&gt;
 background-position: center left;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible-arrowtoggle.mw-collapsible-toggle-collapsed {&lt;br /&gt;
 padding-left: 20px !important;&lt;br /&gt;
 background-image: url(&#039;//upload.wikimedia.org/wikipedia/commons/4/41/MediaWiki_Vector_skin_right_arrow.png&#039;);&lt;br /&gt;
 background-repeat: no-repeat;&lt;br /&gt;
 background-position: center left;&lt;br /&gt;
}&lt;br /&gt;
.action a img&lt;br /&gt;
{ margin: -5px 0 0 -16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.bg1 { background-color: #ffd890 }&lt;br /&gt;
.bg2 { background-color: #b4fff1 }&lt;br /&gt;
.bg3 { background-color: #c6e894 }&lt;br /&gt;
.bg4 { background-color: #e89898 }&lt;br /&gt;
.bg5 { background-color: #c6acff }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Wikipedia */&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe1 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe1, table.hintergrundfarbe1, .hintergrundfarbe1 {&lt;br /&gt;
    background-color: #f9f9f9;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe2 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe2, table.hintergrundfarbe2, .hintergrundfarbe2 {&lt;br /&gt;
    background-color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe3 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe3, table.hintergrundfarbe3, .hintergrundfarbe3 {&lt;br /&gt;
    background-color: #ffff40;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe4 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe4, table.hintergrundfarbe4, .hintergrundfarbe4 {&lt;br /&gt;
    background-color: #fa0;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe5 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe5, table.hintergrundfarbe5, .hintergrundfarbe5 {&lt;br /&gt;
    background-color: #e0e0e0;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe6 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe6, table.hintergrundfarbe6, .hintergrundfarbe6 {&lt;br /&gt;
    background-color: #b3b7ff;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe7 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe7, table.hintergrundfarbe7, .hintergrundfarbe7 {&lt;br /&gt;
    background-color: #ffcbcb;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe8 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe8, table.hintergrundfarbe8, .hintergrundfarbe8 {&lt;br /&gt;
    background-color: #ffebad;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe9 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe9, table.hintergrundfarbe9, .hintergrundfarbe9 {&lt;br /&gt;
    background-color: #b9ffc5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Studienpläne */&lt;br /&gt;
&lt;br /&gt;
.bgdgrey, .bgdgrau&lt;br /&gt;
{ background: #555; }&lt;br /&gt;
&lt;br /&gt;
.bggrey, .bggrau &lt;br /&gt;
{ background: #999; }&lt;br /&gt;
 &lt;br /&gt;
.bgorange&lt;br /&gt;
{ background: #f60; }&lt;br /&gt;
&lt;br /&gt;
.bgblue, .bgblau&lt;br /&gt;
{ background: #36c; }&lt;br /&gt;
&lt;br /&gt;
.bgred, .bgrot&lt;br /&gt;
{ background: #c03; }&lt;br /&gt;
&lt;br /&gt;
.bgdgrey a, .bgdgrau a&lt;br /&gt;
{ color:      #fff !important; }&lt;br /&gt;
&lt;br /&gt;
.bggrey a, .bggrau a&lt;br /&gt;
{ color:      #fff !important; }&lt;br /&gt;
&lt;br /&gt;
.bgorange a&lt;br /&gt;
{ color:      #fff  !important; }&lt;br /&gt;
&lt;br /&gt;
.bgblue a, .bgblau a&lt;br /&gt;
{ color:      #fff  !important; }&lt;br /&gt;
&lt;br /&gt;
.bgred a, .bgrot a&lt;br /&gt;
{ color:      #fff  !important; }&lt;br /&gt;
&lt;br /&gt;
.bgdgrey a, .bgdgrau a&lt;br /&gt;
{ color:      #fff  !important; }&lt;br /&gt;
&lt;br /&gt;
.grey, .grau  &lt;br /&gt;
{ color: #999 }&lt;br /&gt;
&lt;br /&gt;
.dgrey, .dgrau  &lt;br /&gt;
{ color: #666; }&lt;br /&gt;
&lt;br /&gt;
.orange  &lt;br /&gt;
{ color: #f60; }&lt;br /&gt;
 &lt;br /&gt;
.white &lt;br /&gt;
{ color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
.amber &lt;br /&gt;
{ color: #f60; }&lt;br /&gt;
&lt;br /&gt;
.blue, .blau &lt;br /&gt;
{ color: #36c; }&lt;br /&gt;
&lt;br /&gt;
.studienplan&lt;br /&gt;
{ border-collapse: collapse; }&lt;br /&gt;
&lt;br /&gt;
.studienplan tr &lt;br /&gt;
{ height: 1em; }&lt;br /&gt;
&lt;br /&gt;
.studienplan td &lt;br /&gt;
{ border:         1px solid #222;&lt;br /&gt;
  vertical-align: text-top;&lt;br /&gt;
  padding:        5px 5px 5px 5px; &lt;br /&gt;
  width:          auto;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.studienplan td.noborder &lt;br /&gt;
{ border: 0px; }&lt;br /&gt;
&lt;br /&gt;
.studienplan&lt;br /&gt;
{ border-collapse: collapse; }&lt;br /&gt;
&lt;br /&gt;
.studienplan &amp;gt; * &amp;gt; td &lt;br /&gt;
{ border: 1px solid #222; }&lt;br /&gt;
&lt;br /&gt;
table.wikitable&lt;br /&gt;
{ margin: 2ex 0 3ex 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.texttable&lt;br /&gt;
{ margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  border: 0 !important; &lt;br /&gt;
  border-collapse: collapse; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.texttable td&lt;br /&gt;
{ border: 0 !important; &lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.texttable td:first-child&lt;br /&gt;
{ padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.texttable td:first-child ~ td&lt;br /&gt;
{ padding-left: 0.2em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.wikitable &amp;gt; th &lt;br /&gt;
{ background-color: #ddd !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.wikitable td, table.wikitable &amp;gt; th,&lt;br /&gt;
table.wikitext td, table.wikitext &amp;gt; th&lt;br /&gt;
{ text-align: left !important;&lt;br /&gt;
  vertical-align: top !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.wikitable td, table.wikitable &amp;gt; th,&lt;br /&gt;
table.wikitext td, table.wikitext &amp;gt; th&lt;br /&gt;
{ text-align: left !important;&lt;br /&gt;
  vertical-align: top !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
### Tooltips&lt;br /&gt;
&lt;br /&gt;
#tooltip&lt;br /&gt;
{ font-size: 0.875em;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  text-shadow: 0 1px rgba( 0, 0, 0, .5 );&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  background: #333;&lt;br /&gt;
  background: -webkit-gradient( linear, left top, left bottom, from( rgba( 0, 0, 0, .6 ) ), to( rgba( 0, 0, 0, .8 ) ) );&lt;br /&gt;
  background: -webkit-linear-gradient( top, rgba( 0, 0, 0, .6 ), rgba( 0, 0, 0, .8 ) );&lt;br /&gt;
  background: -moz-linear-gradient( top, rgba( 0, 0, 0, .6 ), rgba( 0, 0, 0, .8 ) );&lt;br /&gt;
  background: -ms-radial-gradient( top, rgba( 0, 0, 0, .6 ), rgba( 0, 0, 0, .8 ) );&lt;br /&gt;
  background: -o-linear-gradient( top, rgba( 0, 0, 0, .6 ), rgba( 0, 0, 0, .8 ) );&lt;br /&gt;
  background: linear-gradient( top, rgba( 0, 0, 0, .6 ), rgba( 0, 0, 0, .8 ) );&lt;br /&gt;
  -webkit-border-radius: 5px;&lt;br /&gt;
  -moz-border-radius: 5px;&lt;br /&gt;
  border-radius: 5px;&lt;br /&gt;
  border-top: 1px solid #fff;&lt;br /&gt;
  -webkit-box-shadow: 0 3px 5px rgba( 0, 0, 0, .3 );&lt;br /&gt;
  -moz-box-shadow: 0 3px 5px rgba( 0, 0, 0, .3 );&lt;br /&gt;
  box-shadow: 0 3px 5px rgba( 0, 0, 0, .3 );&lt;br /&gt;
  position: absolute;&lt;br /&gt;
  z-index: 100;&lt;br /&gt;
  padding: 15px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#tooltip:after&lt;br /&gt;
{&lt;br /&gt;
  width: 0;&lt;br /&gt;
  height: 0;&lt;br /&gt;
  border-left: 10px solid transparent;&lt;br /&gt;
  border-right: 10px solid transparent;&lt;br /&gt;
  border-top: 10px solid #333;&lt;br /&gt;
  border-top-color: rgba( 0, 0, 0, .7 );&lt;br /&gt;
  content: &#039;&#039;;&lt;br /&gt;
  position: absolute;&lt;br /&gt;
  left: 50%;&lt;br /&gt;
  bottom: -10px;&lt;br /&gt;
  margin-left: -10px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#tooltip.top:after&lt;br /&gt;
{&lt;br /&gt;
  border-top-color: transparent;&lt;br /&gt;
  border-bottom: 10px solid #333;&lt;br /&gt;
  border-bottom-color: rgba( 0, 0, 0, .6 );&lt;br /&gt;
  top: -20px;&lt;br /&gt;
  bottom: auto;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#tooltip.left:after&lt;br /&gt;
{&lt;br /&gt;
  left: 10px;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#tooltip.right:after&lt;br /&gt;
{&lt;br /&gt;
  right: 10px;&lt;br /&gt;
  left: auto;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
abbr[title]&lt;br /&gt;
{&lt;br /&gt;
  border-bottom: 0 !important&lt;br /&gt;
}&lt;br /&gt;
*/&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=MediaWiki:Common.css&amp;diff=53277</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=MediaWiki:Common.css&amp;diff=53277"/>
		<updated>2026-01-20T07:57:58Z</updated>

		<summary type="html">&lt;p&gt;Kowa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* CSS an dieser Stelle wirkt sich auf alle Skins aus */&lt;br /&gt;
&lt;br /&gt;
:target::before&lt;br /&gt;
{ content:&amp;quot;&amp;quot;; &lt;br /&gt;
  position:relative; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#catlinks li a&lt;br /&gt;
{ background: #DDD !important; }&lt;br /&gt;
&lt;br /&gt;
a.mw-selflink&lt;br /&gt;
{&lt;br /&gt;
  color: #333;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
a.bgorange:visited, a.bgrot:visited, a.bgblue:visited, a.bgdgrau:visited, a.bggrau:visited&lt;br /&gt;
{&lt;br /&gt;
  color: white !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
a:visited, a.new:visited, #p-personal a.new:visited, a.external:visited, a code&lt;br /&gt;
{&lt;br /&gt;
  color: #337ab7 !important;&lt;br /&gt;
}&lt;br /&gt;
*/&lt;br /&gt;
a:visited, #p-personal a.new:visited, a.external:visited, a code&lt;br /&gt;
{&lt;br /&gt;
  color: #337ab7 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
h5&lt;br /&gt;
{&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
p&lt;br /&gt;
{&lt;br /&gt;
  max-width: 45em;&lt;br /&gt;
  overflow: hidden&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
p.no-max-width&lt;br /&gt;
{&lt;br /&gt;
  max-width: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
pre, code, tt, .mw-geshi&lt;br /&gt;
{&lt;br /&gt;
  font-family: consolas, &amp;quot;courier new&amp;quot;, courier, monospace;&lt;br /&gt;
  font-size:  100%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.indent&lt;br /&gt;
{&lt;br /&gt;
  display: block;&lt;br /&gt;
  margin:  0 0 0 1em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.table_biblio&lt;br /&gt;
{&lt;br /&gt;
  margin:    0 0 1em 1em; &lt;br /&gt;
  font-size: 95%;&lt;br /&gt;
  max-width: 45em; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.table_biblio td&lt;br /&gt;
{&lt;br /&gt;
  padding: 4;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.table_no_border&lt;br /&gt;
{&lt;br /&gt;
  margin: 1em 1em 1em 0;&lt;br /&gt;
  border: 0;&lt;br /&gt;
  background: white;&lt;br /&gt;
  border-collapse: collapse&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.subtable&lt;br /&gt;
{&lt;br /&gt;
  text-align:left;&lt;br /&gt;
  background-color: #EEEEFF !important;&lt;br /&gt;
  font-weight:bold&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable, &lt;br /&gt;
.datatable-rt, &lt;br /&gt;
.datatable-modules, &lt;br /&gt;
.datatable-sql&lt;br /&gt;
{&lt;br /&gt;
  margin: 1ex 1em 1ex 0;&lt;br /&gt;
  border:1px #aaa solid;&lt;br /&gt;
  background:white;&lt;br /&gt;
  border-collapse:collapse;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable th, .datatable td,&lt;br /&gt;
.datatable-rt th,.datatable-rt td, &lt;br /&gt;
.datatable-modules th,.datatable-modules td,&lt;br /&gt;
.datatable-sql th,.datatable-sql td &lt;br /&gt;
{&lt;br /&gt;
  border:1px #aaa solid;&lt;br /&gt;
  padding:1ex&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable-sql th,.datatable-sql td &lt;br /&gt;
{&lt;br /&gt;
  font-family: consolas, &amp;quot;courier new&amp;quot;, courier, monospace;&lt;br /&gt;
  font-size:  95%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable tbody tr:nth-child(odd)  td, &lt;br /&gt;
.datatable-rt tbody tr:nth-child(odd)  td, &lt;br /&gt;
.datatable-sql tbody tr:nth-child(odd)  td &lt;br /&gt;
{  &lt;br /&gt;
   background-color: #eee; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable-modules tbody tr:nth-child(odd)  td&lt;br /&gt;
{  &lt;br /&gt;
   background-color: #fcfcfc; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable th, &lt;br /&gt;
.datatable-rt th, &lt;br /&gt;
.datatable-modules th, &lt;br /&gt;
.datatable-sql th&lt;br /&gt;
{ &lt;br /&gt;
  vertical-align:top;&lt;br /&gt;
  text-align:left;&lt;br /&gt;
  background-color: #ccc;&lt;br /&gt;
  font-weight:bold&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable td, &lt;br /&gt;
.datatable-rt td, &lt;br /&gt;
.datatable-modules td, &lt;br /&gt;
.datatable-sql td&lt;br /&gt;
{&lt;br /&gt;
  vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.formtable td, .formtable th&lt;br /&gt;
{&lt;br /&gt;
  vertical-align: middle !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 47.9375em) {&lt;br /&gt;
  .datatable-rt {&lt;br /&gt;
    border:0;&lt;br /&gt;
    padding-bottom: 1.4375em;&lt;br /&gt;
    padding-bottom: 0;&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    /*&lt;br /&gt;
     * make everything display block so it &lt;br /&gt;
     * aligns vertically&lt;br /&gt;
     */&lt;br /&gt;
    /* Labeling&lt;br /&gt;
     * adding a data-title attribute to the cells&lt;br /&gt;
     * lets us add text before the content to provide&lt;br /&gt;
     * the missing context&lt;br /&gt;
     * &lt;br /&gt;
     * Markup: &lt;br /&gt;
     *   &amp;lt;td data-title=&amp;quot;Column Header&amp;quot;&amp;gt;Content Here&amp;lt;/td&amp;gt;&lt;br /&gt;
     * &lt;br /&gt;
     * Display:&lt;br /&gt;
     *   Column Header: Content Here&lt;br /&gt;
     */&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt caption {&lt;br /&gt;
    display: block;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt thead, .datatable-rt tbody tr:first-child {&lt;br /&gt;
    display: none;&lt;br /&gt;
    visibility: hidden;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt tbody {&lt;br /&gt;
    border:0;&lt;br /&gt;
    display: block;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    white-space: normal;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt tr, .datatable-rt td {&lt;br /&gt;
    border:1px #aaa solid;&lt;br /&gt;
    display: block;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    white-space: normal;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt tr {&lt;br /&gt;
    margin-bottom: 1.5em;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt th[data-title]:before,&lt;br /&gt;
  table td[data-title]:before {&lt;br /&gt;
    content: attr(data-title) &amp;quot;\a&amp;quot;;&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
    white-space: pre;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt th:not([data-title]) {&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt td:empty {&lt;br /&gt;
    display: none;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.citetable&lt;br /&gt;
{&lt;br /&gt;
  margin: 1em 1em 1em 0;&lt;br /&gt;
  border:1px #aaa solid;&lt;br /&gt;
  background:white;&lt;br /&gt;
  border-collapse:collapse;&lt;br /&gt;
}&lt;br /&gt;
.citetable th,.citetable td&lt;br /&gt;
{&lt;br /&gt;
  border:1px #aaa solid;&lt;br /&gt;
  padding:1ex;&lt;br /&gt;
  max-width: 400px; &lt;br /&gt;
  word-wrap: break-word;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.citetable th&lt;br /&gt;
{&lt;br /&gt;
  text-align:center;&lt;br /&gt;
  background-color: #EEEEFF;&lt;br /&gt;
  font-weight:bold&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.citetable td&lt;br /&gt;
{&lt;br /&gt;
  vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.menutable&lt;br /&gt;
{&lt;br /&gt;
  margin: 0 0 1em 0;&lt;br /&gt;
  border: 0;&lt;br /&gt;
  background: white;&lt;br /&gt;
  border-collapse:collapse&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.menutable p&lt;br /&gt;
{&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
.mw-content-ltr&lt;br /&gt;
{&lt;br /&gt;
    margin-top: 1em;&lt;br /&gt;
    margin-bottom: 1em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-code&lt;br /&gt;
{&lt;br /&gt;
  font-family: consolas, &amp;quot;courier new&amp;quot;, courier, monospace;&lt;br /&gt;
  font-size:  95%;&lt;br /&gt;
  max-width: 57em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
pre&lt;br /&gt;
{&lt;br /&gt;
  max-width: 57em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
code &lt;br /&gt;
{&lt;br /&gt;
    background-color: #FFF;&lt;br /&gt;
    border: 0;&lt;br /&gt;
    border-radius: 0;&lt;br /&gt;
    padding: 0 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
td&lt;br /&gt;
{ &lt;br /&gt;
  padding: 0.2em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#toc ul&lt;br /&gt;
{&lt;br /&gt;
  margin-left: -0.5em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent  ul&lt;br /&gt;
{&lt;br /&gt;
  list-style-type: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent  ul, #bodyContent  ol&lt;br /&gt;
{&lt;br /&gt;
   overflow: hidden;&lt;br /&gt;
   max-width: 43.4em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-body a, .mw-body a:visited, .mw-body a.new:visited&lt;br /&gt;
{&lt;br /&gt;
  color: #337ab7;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-body ul&lt;br /&gt;
{&lt;br /&gt;
  list-style: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-body ul, .mw-body ol&lt;br /&gt;
{&lt;br /&gt;
  padding-left: 0ex !important;&lt;br /&gt;
  padding-bottom: 0ex !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent .inputSpan ul &amp;gt; li::before,  &lt;br /&gt;
#bodyContent .redirectMsg ul &amp;gt; li::before, &lt;br /&gt;
li#mw-imagepage-reupload-link::before,&lt;br /&gt;
li#mw-imagepage-upload-disallowed::before&lt;br /&gt;
{&lt;br /&gt;
  content: &amp;quot;&amp;quot; !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
li#mw-imagepage-reupload-link .plainlinks,&lt;br /&gt;
li#mw-imagepage-upload-disallowed&lt;br /&gt;
{&lt;br /&gt;
  text-indent: -1.6em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent ul &amp;gt; li::before &lt;br /&gt;
{&lt;br /&gt;
  content: &amp;quot;\2013 \0020&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.select2-container-multi .select2-search-choice-close {&lt;br /&gt;
    left: -15px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent  ol &amp;gt; li&lt;br /&gt;
{&lt;br /&gt;
   margin-left: 1.6em;&lt;br /&gt;
}&lt;br /&gt;
#bodyContent  ul &amp;gt; li&lt;br /&gt;
{&lt;br /&gt;
   text-indent: -0.9em;&lt;br /&gt;
   margin-left: 1.6em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent ul &amp;gt; li.gallerybox&lt;br /&gt;
{&lt;br /&gt;
   text-indent: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent .rightfloat  ul &amp;gt; li&lt;br /&gt;
{&lt;br /&gt;
   margin-left: 3.9em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent #mw-subcategories ul &amp;gt; li::before,&lt;br /&gt;
#toc ul &amp;gt; li::before, &lt;br /&gt;
.toc ul &amp;gt; li::before, &lt;br /&gt;
#bodyContent  .ms-list &amp;gt; li::before,&lt;br /&gt;
#bodyContent .gallerybox::before, &lt;br /&gt;
#bodyContent  ul#filetoc &amp;gt; li::before&lt;br /&gt;
{&lt;br /&gt;
  content: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-search-profile-tabs .search-types a&lt;br /&gt;
{&lt;br /&gt;
  display: inline !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-search-result-heading&lt;br /&gt;
{&lt;br /&gt;
   display: inline !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-search-results .searchresult, .mw-search-result-data&lt;br /&gt;
{&lt;br /&gt;
   text-indent: 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
li&lt;br /&gt;
{&lt;br /&gt;
  max-width: 50em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
dd&lt;br /&gt;
{&lt;br /&gt;
  max-width: 50em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.thumbinner&lt;br /&gt;
{&lt;br /&gt;
  margin-bottom: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-headline &lt;br /&gt;
{ &lt;br /&gt;
  padding-top: 0px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#mw-createaccount-cta, #pt-createaccount&lt;br /&gt;
{&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-highlight&lt;br /&gt;
{&lt;br /&gt;
    background-color: #ffffff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_quality&lt;br /&gt;
{&lt;br /&gt;
  margin: 0px 0px 15px 0px;&lt;br /&gt;
  padding: 0.2ex;&lt;br /&gt;
  font-size: 75%;&lt;br /&gt;
  background-color: #DDD;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_quality p&lt;br /&gt;
{&lt;br /&gt;
  padding-left: 1ex;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_quality table&lt;br /&gt;
{&lt;br /&gt;
  margin: 0 1px 1px 0;&lt;br /&gt;
}&lt;br /&gt;
.wiki_quality td&lt;br /&gt;
{&lt;br /&gt;
  border: 1px #DDD solid !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_red&lt;br /&gt;
{&lt;br /&gt;
    background-color: #FFA288 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_yellow&lt;br /&gt;
{&lt;br /&gt;
    background-color: #FFECAD !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_green&lt;br /&gt;
{&lt;br /&gt;
    background-color: #96D1AD !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_warning_01&lt;br /&gt;
{&lt;br /&gt;
   margin: 0px 0px 7px 0px;&lt;br /&gt;
   padding: 1ex;&lt;br /&gt;
   border: 5px solid #FFE489;&lt;br /&gt;
   font-size: 95%&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_warning_02&lt;br /&gt;
{&lt;br /&gt;
   margin: 0px 0px 7px 0px;&lt;br /&gt;
   padding: 1ex;&lt;br /&gt;
   border: 5px solid #FFA288;&lt;br /&gt;
   font-size: 95%&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_highlight_01&lt;br /&gt;
{&lt;br /&gt;
   margin: 0px 0px 7px 0px;&lt;br /&gt;
   padding: 1ex;&lt;br /&gt;
   border: 5px solid #96D1AD;&lt;br /&gt;
   font-size: 95%&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_disambiguation_01&lt;br /&gt;
{&lt;br /&gt;
  border: 1px solid #aaa;&lt;br /&gt;
  border-width:2px 0 2px 0;&lt;br /&gt;
  background-color: #f9f9f9;&lt;br /&gt;
  padding: 5px 5px 0px 5px;&lt;br /&gt;
  margin: 0px 0px 7px 0px;&lt;br /&gt;
  font-size:100%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.inline * {display:inline}&lt;br /&gt;
&lt;br /&gt;
.mw-codeX&lt;br /&gt;
{&lt;br /&gt;
  max-width: 48em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.formInput&lt;br /&gt;
{ &lt;br /&gt;
  width: 30em&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.formula&lt;br /&gt;
{&lt;br /&gt;
  display: block;&lt;br /&gt;
  margin:  0 0 1ex 1em;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.regular&lt;br /&gt;
{&lt;br /&gt;
  font-style: regular;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.quote&lt;br /&gt;
{&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-style: italic;&lt;br /&gt;
  margin:  0 0 1ex 1em;&lt;br /&gt;
  max-width: 43em;&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.quote em, .quote i, i em, em em, em i&lt;br /&gt;
{&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.quote2&lt;br /&gt;
{&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
  margin:  0 0 0 1em;&lt;br /&gt;
  max-width: 44em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
div.floatright&lt;br /&gt;
{&lt;br /&gt;
  clear: right;&lt;br /&gt;
  float: right;&lt;br /&gt;
  margin: 0.5em 0 0.8em 1.4em;&lt;br /&gt;
  position: relative;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.floatleft&lt;br /&gt;
{&lt;br /&gt;
  clear: left;&lt;br /&gt;
  float: left;&lt;br /&gt;
  margin: 0.5em 0 0.8em 1.4em;&lt;br /&gt;
  position: relative;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-code &lt;br /&gt;
{&lt;br /&gt;
  margin:  0 0 1ex 1em;&lt;br /&gt;
}&lt;br /&gt;
/* Add arrows to toggle-blocks for collapsible elements */&lt;br /&gt;
.mw-collapsible-arrowtoggle.mw-collapsible-toggle-expanded {&lt;br /&gt;
 padding-left: 20px !important;&lt;br /&gt;
 background-image: url(&#039;//upload.wikimedia.&lt;br /&gt;
org/wikipedia/commons/1/10/MediaWiki_Vector_skin_action_arrow.png&#039;);&lt;br /&gt;
 background-repeat: no-repeat;&lt;br /&gt;
 background-position: center left;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible-arrowtoggle.mw-collapsible-toggle-collapsed {&lt;br /&gt;
 padding-left: 20px !important;&lt;br /&gt;
 background-image: url(&#039;//upload.wikimedia.org/wikipedia/commons/4/41/MediaWiki_Vector_skin_right_arrow.png&#039;);&lt;br /&gt;
 background-repeat: no-repeat;&lt;br /&gt;
 background-position: center left;&lt;br /&gt;
}&lt;br /&gt;
.action a img&lt;br /&gt;
{ margin: -5px 0 0 -16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.bg1 { background-color: #ffd890 }&lt;br /&gt;
.bg2 { background-color: #b4fff1 }&lt;br /&gt;
.bg3 { background-color: #c6e894 }&lt;br /&gt;
.bg4 { background-color: #e89898 }&lt;br /&gt;
.bg5 { background-color: #c6acff }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Wikipedia */&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe1 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe1, table.hintergrundfarbe1, .hintergrundfarbe1 {&lt;br /&gt;
    background-color: #f9f9f9;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe2 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe2, table.hintergrundfarbe2, .hintergrundfarbe2 {&lt;br /&gt;
    background-color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe3 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe3, table.hintergrundfarbe3, .hintergrundfarbe3 {&lt;br /&gt;
    background-color: #ffff40;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe4 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe4, table.hintergrundfarbe4, .hintergrundfarbe4 {&lt;br /&gt;
    background-color: #fa0;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe5 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe5, table.hintergrundfarbe5, .hintergrundfarbe5 {&lt;br /&gt;
    background-color: #e0e0e0;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe6 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe6, table.hintergrundfarbe6, .hintergrundfarbe6 {&lt;br /&gt;
    background-color: #b3b7ff;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe7 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe7, table.hintergrundfarbe7, .hintergrundfarbe7 {&lt;br /&gt;
    background-color: #ffcbcb;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe8 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe8, table.hintergrundfarbe8, .hintergrundfarbe8 {&lt;br /&gt;
    background-color: #ffebad;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe9 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe9, table.hintergrundfarbe9, .hintergrundfarbe9 {&lt;br /&gt;
    background-color: #b9ffc5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Studienpläne */&lt;br /&gt;
&lt;br /&gt;
.bgdgrey, .bgdgrau&lt;br /&gt;
{ background: #555; }&lt;br /&gt;
&lt;br /&gt;
.bggrey, .bggrau &lt;br /&gt;
{ background: #999; }&lt;br /&gt;
 &lt;br /&gt;
.bgorange&lt;br /&gt;
{ background: #f60; }&lt;br /&gt;
&lt;br /&gt;
.bgblue, .bgblau&lt;br /&gt;
{ background: #36c; }&lt;br /&gt;
&lt;br /&gt;
.bgred, .bgrot&lt;br /&gt;
{ background: #c03; }&lt;br /&gt;
&lt;br /&gt;
.bgdgrey a, .bgdgrau a&lt;br /&gt;
{ color:      #fff !important; }&lt;br /&gt;
&lt;br /&gt;
.bggrey a, .bggrau a&lt;br /&gt;
{ color:      #fff !important; }&lt;br /&gt;
&lt;br /&gt;
.bgorange a&lt;br /&gt;
{ color:      #fff  !important; }&lt;br /&gt;
&lt;br /&gt;
.bgblue a, .bgblau a&lt;br /&gt;
{ color:      #fff  !important; }&lt;br /&gt;
&lt;br /&gt;
.bgred a, .bgrot a&lt;br /&gt;
{ color:      #fff  !important; }&lt;br /&gt;
&lt;br /&gt;
.bgdgrey a, .bgdgrau a&lt;br /&gt;
{ color:      #fff  !important; }&lt;br /&gt;
&lt;br /&gt;
.grey, .grau  &lt;br /&gt;
{ color: #999 }&lt;br /&gt;
&lt;br /&gt;
.dgrey, .dgrau  &lt;br /&gt;
{ color: #666; }&lt;br /&gt;
&lt;br /&gt;
.orange  &lt;br /&gt;
{ color: #f60; }&lt;br /&gt;
 &lt;br /&gt;
.white &lt;br /&gt;
{ color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
.amber &lt;br /&gt;
{ color: #f60; }&lt;br /&gt;
&lt;br /&gt;
.blue, .blau &lt;br /&gt;
{ color: #36c; }&lt;br /&gt;
&lt;br /&gt;
.studienplan&lt;br /&gt;
{ border-collapse: collapse; }&lt;br /&gt;
&lt;br /&gt;
.studienplan tr &lt;br /&gt;
{ height: 1em; }&lt;br /&gt;
&lt;br /&gt;
.studienplan td &lt;br /&gt;
{ border:         1px solid #222;&lt;br /&gt;
  vertical-align: text-top;&lt;br /&gt;
  padding:        5px 5px 5px 5px; &lt;br /&gt;
  width:          auto;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.studienplan td.noborder &lt;br /&gt;
{ border: 0px; }&lt;br /&gt;
&lt;br /&gt;
.studienplan&lt;br /&gt;
{ border-collapse: collapse; }&lt;br /&gt;
&lt;br /&gt;
.studienplan &amp;gt; * &amp;gt; td &lt;br /&gt;
{ border: 1px solid #222; }&lt;br /&gt;
&lt;br /&gt;
table.wikitable&lt;br /&gt;
{ margin: 2ex 0 3ex 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.texttable&lt;br /&gt;
{ margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  border: 0 !important; &lt;br /&gt;
  border-collapse: collapse; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.texttable td&lt;br /&gt;
{ border: 0 !important; &lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.texttable td:first-child&lt;br /&gt;
{ padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.texttable td:first-child ~ td&lt;br /&gt;
{ padding-left: 0.2em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.wikitable &amp;gt; th &lt;br /&gt;
{ background-color: #ddd !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.wikitable td, table.wikitable &amp;gt; th,&lt;br /&gt;
table.wikitext td, table.wikitext &amp;gt; th&lt;br /&gt;
{ text-align: left !important;&lt;br /&gt;
  vertical-align: top !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.wikitable td, table.wikitable &amp;gt; th,&lt;br /&gt;
table.wikitext td, table.wikitext &amp;gt; th&lt;br /&gt;
{ text-align: left !important;&lt;br /&gt;
  vertical-align: top !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
### Tooltips&lt;br /&gt;
&lt;br /&gt;
#tooltip&lt;br /&gt;
{ font-size: 0.875em;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  text-shadow: 0 1px rgba( 0, 0, 0, .5 );&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  background: #333;&lt;br /&gt;
  background: -webkit-gradient( linear, left top, left bottom, from( rgba( 0, 0, 0, .6 ) ), to( rgba( 0, 0, 0, .8 ) ) );&lt;br /&gt;
  background: -webkit-linear-gradient( top, rgba( 0, 0, 0, .6 ), rgba( 0, 0, 0, .8 ) );&lt;br /&gt;
  background: -moz-linear-gradient( top, rgba( 0, 0, 0, .6 ), rgba( 0, 0, 0, .8 ) );&lt;br /&gt;
  background: -ms-radial-gradient( top, rgba( 0, 0, 0, .6 ), rgba( 0, 0, 0, .8 ) );&lt;br /&gt;
  background: -o-linear-gradient( top, rgba( 0, 0, 0, .6 ), rgba( 0, 0, 0, .8 ) );&lt;br /&gt;
  background: linear-gradient( top, rgba( 0, 0, 0, .6 ), rgba( 0, 0, 0, .8 ) );&lt;br /&gt;
  -webkit-border-radius: 5px;&lt;br /&gt;
  -moz-border-radius: 5px;&lt;br /&gt;
  border-radius: 5px;&lt;br /&gt;
  border-top: 1px solid #fff;&lt;br /&gt;
  -webkit-box-shadow: 0 3px 5px rgba( 0, 0, 0, .3 );&lt;br /&gt;
  -moz-box-shadow: 0 3px 5px rgba( 0, 0, 0, .3 );&lt;br /&gt;
  box-shadow: 0 3px 5px rgba( 0, 0, 0, .3 );&lt;br /&gt;
  position: absolute;&lt;br /&gt;
  z-index: 100;&lt;br /&gt;
  padding: 15px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#tooltip:after&lt;br /&gt;
{&lt;br /&gt;
  width: 0;&lt;br /&gt;
  height: 0;&lt;br /&gt;
  border-left: 10px solid transparent;&lt;br /&gt;
  border-right: 10px solid transparent;&lt;br /&gt;
  border-top: 10px solid #333;&lt;br /&gt;
  border-top-color: rgba( 0, 0, 0, .7 );&lt;br /&gt;
  content: &#039;&#039;;&lt;br /&gt;
  position: absolute;&lt;br /&gt;
  left: 50%;&lt;br /&gt;
  bottom: -10px;&lt;br /&gt;
  margin-left: -10px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#tooltip.top:after&lt;br /&gt;
{&lt;br /&gt;
  border-top-color: transparent;&lt;br /&gt;
  border-bottom: 10px solid #333;&lt;br /&gt;
  border-bottom-color: rgba( 0, 0, 0, .6 );&lt;br /&gt;
  top: -20px;&lt;br /&gt;
  bottom: auto;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#tooltip.left:after&lt;br /&gt;
{&lt;br /&gt;
  left: 10px;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#tooltip.right:after&lt;br /&gt;
{&lt;br /&gt;
  right: 10px;&lt;br /&gt;
  left: auto;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
abbr[title]&lt;br /&gt;
{&lt;br /&gt;
  border-bottom: 0 !important&lt;br /&gt;
}&lt;br /&gt;
*/&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=MediaWiki:Common.css&amp;diff=53276</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=MediaWiki:Common.css&amp;diff=53276"/>
		<updated>2026-01-20T07:56:05Z</updated>

		<summary type="html">&lt;p&gt;Kowa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* CSS an dieser Stelle wirkt sich auf alle Skins aus */&lt;br /&gt;
&lt;br /&gt;
:target::before&lt;br /&gt;
{ content:&amp;quot;&amp;quot;; &lt;br /&gt;
  position:relative; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#catlinks li a&lt;br /&gt;
{ background: #DDD !important; }&lt;br /&gt;
&lt;br /&gt;
a.mw-selflink&lt;br /&gt;
{&lt;br /&gt;
  color: #333;&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
a.bgorange, a.bgrot, a.bgblue, a.bgdgrau, a.bggrau&lt;br /&gt;
{&lt;br /&gt;
  color: white !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
a:visited, a.new:visited, #p-personal a.new:visited, a.external:visited, a code&lt;br /&gt;
{&lt;br /&gt;
  color: #337ab7 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
h5&lt;br /&gt;
{&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
p&lt;br /&gt;
{&lt;br /&gt;
  max-width: 45em;&lt;br /&gt;
  overflow: hidden&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
p.no-max-width&lt;br /&gt;
{&lt;br /&gt;
  max-width: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
pre, code, tt, .mw-geshi&lt;br /&gt;
{&lt;br /&gt;
  font-family: consolas, &amp;quot;courier new&amp;quot;, courier, monospace;&lt;br /&gt;
  font-size:  100%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.indent&lt;br /&gt;
{&lt;br /&gt;
  display: block;&lt;br /&gt;
  margin:  0 0 0 1em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.table_biblio&lt;br /&gt;
{&lt;br /&gt;
  margin:    0 0 1em 1em; &lt;br /&gt;
  font-size: 95%;&lt;br /&gt;
  max-width: 45em; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.table_biblio td&lt;br /&gt;
{&lt;br /&gt;
  padding: 4;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.table_no_border&lt;br /&gt;
{&lt;br /&gt;
  margin: 1em 1em 1em 0;&lt;br /&gt;
  border: 0;&lt;br /&gt;
  background: white;&lt;br /&gt;
  border-collapse: collapse&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.subtable&lt;br /&gt;
{&lt;br /&gt;
  text-align:left;&lt;br /&gt;
  background-color: #EEEEFF !important;&lt;br /&gt;
  font-weight:bold&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable, &lt;br /&gt;
.datatable-rt, &lt;br /&gt;
.datatable-modules, &lt;br /&gt;
.datatable-sql&lt;br /&gt;
{&lt;br /&gt;
  margin: 1ex 1em 1ex 0;&lt;br /&gt;
  border:1px #aaa solid;&lt;br /&gt;
  background:white;&lt;br /&gt;
  border-collapse:collapse;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable th, .datatable td,&lt;br /&gt;
.datatable-rt th,.datatable-rt td, &lt;br /&gt;
.datatable-modules th,.datatable-modules td,&lt;br /&gt;
.datatable-sql th,.datatable-sql td &lt;br /&gt;
{&lt;br /&gt;
  border:1px #aaa solid;&lt;br /&gt;
  padding:1ex&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable-sql th,.datatable-sql td &lt;br /&gt;
{&lt;br /&gt;
  font-family: consolas, &amp;quot;courier new&amp;quot;, courier, monospace;&lt;br /&gt;
  font-size:  95%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable tbody tr:nth-child(odd)  td, &lt;br /&gt;
.datatable-rt tbody tr:nth-child(odd)  td, &lt;br /&gt;
.datatable-sql tbody tr:nth-child(odd)  td &lt;br /&gt;
{  &lt;br /&gt;
   background-color: #eee; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable-modules tbody tr:nth-child(odd)  td&lt;br /&gt;
{  &lt;br /&gt;
   background-color: #fcfcfc; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable th, &lt;br /&gt;
.datatable-rt th, &lt;br /&gt;
.datatable-modules th, &lt;br /&gt;
.datatable-sql th&lt;br /&gt;
{ &lt;br /&gt;
  vertical-align:top;&lt;br /&gt;
  text-align:left;&lt;br /&gt;
  background-color: #ccc;&lt;br /&gt;
  font-weight:bold&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.datatable td, &lt;br /&gt;
.datatable-rt td, &lt;br /&gt;
.datatable-modules td, &lt;br /&gt;
.datatable-sql td&lt;br /&gt;
{&lt;br /&gt;
  vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.formtable td, .formtable th&lt;br /&gt;
{&lt;br /&gt;
  vertical-align: middle !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media (max-width: 47.9375em) {&lt;br /&gt;
  .datatable-rt {&lt;br /&gt;
    border:0;&lt;br /&gt;
    padding-bottom: 1.4375em;&lt;br /&gt;
    padding-bottom: 0;&lt;br /&gt;
    display: block;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    /*&lt;br /&gt;
     * make everything display block so it &lt;br /&gt;
     * aligns vertically&lt;br /&gt;
     */&lt;br /&gt;
    /* Labeling&lt;br /&gt;
     * adding a data-title attribute to the cells&lt;br /&gt;
     * lets us add text before the content to provide&lt;br /&gt;
     * the missing context&lt;br /&gt;
     * &lt;br /&gt;
     * Markup: &lt;br /&gt;
     *   &amp;lt;td data-title=&amp;quot;Column Header&amp;quot;&amp;gt;Content Here&amp;lt;/td&amp;gt;&lt;br /&gt;
     * &lt;br /&gt;
     * Display:&lt;br /&gt;
     *   Column Header: Content Here&lt;br /&gt;
     */&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt caption {&lt;br /&gt;
    display: block;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt thead, .datatable-rt tbody tr:first-child {&lt;br /&gt;
    display: none;&lt;br /&gt;
    visibility: hidden;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt tbody {&lt;br /&gt;
    border:0;&lt;br /&gt;
    display: block;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    white-space: normal;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt tr, .datatable-rt td {&lt;br /&gt;
    border:1px #aaa solid;&lt;br /&gt;
    display: block;&lt;br /&gt;
    padding: 0;&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    white-space: normal;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt tr {&lt;br /&gt;
    margin-bottom: 1.5em;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt th[data-title]:before,&lt;br /&gt;
  table td[data-title]:before {&lt;br /&gt;
    content: attr(data-title) &amp;quot;\a&amp;quot;;&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
    white-space: pre;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt th:not([data-title]) {&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
  }&lt;br /&gt;
  .datatable-rt td:empty {&lt;br /&gt;
    display: none;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.citetable&lt;br /&gt;
{&lt;br /&gt;
  margin: 1em 1em 1em 0;&lt;br /&gt;
  border:1px #aaa solid;&lt;br /&gt;
  background:white;&lt;br /&gt;
  border-collapse:collapse;&lt;br /&gt;
}&lt;br /&gt;
.citetable th,.citetable td&lt;br /&gt;
{&lt;br /&gt;
  border:1px #aaa solid;&lt;br /&gt;
  padding:1ex;&lt;br /&gt;
  max-width: 400px; &lt;br /&gt;
  word-wrap: break-word;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.citetable th&lt;br /&gt;
{&lt;br /&gt;
  text-align:center;&lt;br /&gt;
  background-color: #EEEEFF;&lt;br /&gt;
  font-weight:bold&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.citetable td&lt;br /&gt;
{&lt;br /&gt;
  vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.menutable&lt;br /&gt;
{&lt;br /&gt;
  margin: 0 0 1em 0;&lt;br /&gt;
  border: 0;&lt;br /&gt;
  background: white;&lt;br /&gt;
  border-collapse:collapse&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.menutable p&lt;br /&gt;
{&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
.mw-content-ltr&lt;br /&gt;
{&lt;br /&gt;
    margin-top: 1em;&lt;br /&gt;
    margin-bottom: 1em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-code&lt;br /&gt;
{&lt;br /&gt;
  font-family: consolas, &amp;quot;courier new&amp;quot;, courier, monospace;&lt;br /&gt;
  font-size:  95%;&lt;br /&gt;
  max-width: 57em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
pre&lt;br /&gt;
{&lt;br /&gt;
  max-width: 57em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
code &lt;br /&gt;
{&lt;br /&gt;
    background-color: #FFF;&lt;br /&gt;
    border: 0;&lt;br /&gt;
    border-radius: 0;&lt;br /&gt;
    padding: 0 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
td&lt;br /&gt;
{ &lt;br /&gt;
  padding: 0.2em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#toc ul&lt;br /&gt;
{&lt;br /&gt;
  margin-left: -0.5em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent  ul&lt;br /&gt;
{&lt;br /&gt;
  list-style-type: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent  ul, #bodyContent  ol&lt;br /&gt;
{&lt;br /&gt;
   overflow: hidden;&lt;br /&gt;
   max-width: 43.4em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-body a, .mw-body a:visited, .mw-body a.new:visited&lt;br /&gt;
{&lt;br /&gt;
  color: #337ab7;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-body ul&lt;br /&gt;
{&lt;br /&gt;
  list-style: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-body ul, .mw-body ol&lt;br /&gt;
{&lt;br /&gt;
  padding-left: 0ex !important;&lt;br /&gt;
  padding-bottom: 0ex !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent .inputSpan ul &amp;gt; li::before,  &lt;br /&gt;
#bodyContent .redirectMsg ul &amp;gt; li::before, &lt;br /&gt;
li#mw-imagepage-reupload-link::before,&lt;br /&gt;
li#mw-imagepage-upload-disallowed::before&lt;br /&gt;
{&lt;br /&gt;
  content: &amp;quot;&amp;quot; !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
li#mw-imagepage-reupload-link .plainlinks,&lt;br /&gt;
li#mw-imagepage-upload-disallowed&lt;br /&gt;
{&lt;br /&gt;
  text-indent: -1.6em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent ul &amp;gt; li::before &lt;br /&gt;
{&lt;br /&gt;
  content: &amp;quot;\2013 \0020&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.select2-container-multi .select2-search-choice-close {&lt;br /&gt;
    left: -15px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent  ol &amp;gt; li&lt;br /&gt;
{&lt;br /&gt;
   margin-left: 1.6em;&lt;br /&gt;
}&lt;br /&gt;
#bodyContent  ul &amp;gt; li&lt;br /&gt;
{&lt;br /&gt;
   text-indent: -0.9em;&lt;br /&gt;
   margin-left: 1.6em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent ul &amp;gt; li.gallerybox&lt;br /&gt;
{&lt;br /&gt;
   text-indent: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent .rightfloat  ul &amp;gt; li&lt;br /&gt;
{&lt;br /&gt;
   margin-left: 3.9em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#bodyContent #mw-subcategories ul &amp;gt; li::before,&lt;br /&gt;
#toc ul &amp;gt; li::before, &lt;br /&gt;
.toc ul &amp;gt; li::before, &lt;br /&gt;
#bodyContent  .ms-list &amp;gt; li::before,&lt;br /&gt;
#bodyContent .gallerybox::before, &lt;br /&gt;
#bodyContent  ul#filetoc &amp;gt; li::before&lt;br /&gt;
{&lt;br /&gt;
  content: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-search-profile-tabs .search-types a&lt;br /&gt;
{&lt;br /&gt;
  display: inline !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-search-result-heading&lt;br /&gt;
{&lt;br /&gt;
   display: inline !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-search-results .searchresult, .mw-search-result-data&lt;br /&gt;
{&lt;br /&gt;
   text-indent: 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
li&lt;br /&gt;
{&lt;br /&gt;
  max-width: 50em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
dd&lt;br /&gt;
{&lt;br /&gt;
  max-width: 50em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.thumbinner&lt;br /&gt;
{&lt;br /&gt;
  margin-bottom: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-headline &lt;br /&gt;
{ &lt;br /&gt;
  padding-top: 0px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#mw-createaccount-cta, #pt-createaccount&lt;br /&gt;
{&lt;br /&gt;
  display: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-highlight&lt;br /&gt;
{&lt;br /&gt;
    background-color: #ffffff !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_quality&lt;br /&gt;
{&lt;br /&gt;
  margin: 0px 0px 15px 0px;&lt;br /&gt;
  padding: 0.2ex;&lt;br /&gt;
  font-size: 75%;&lt;br /&gt;
  background-color: #DDD;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_quality p&lt;br /&gt;
{&lt;br /&gt;
  padding-left: 1ex;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_quality table&lt;br /&gt;
{&lt;br /&gt;
  margin: 0 1px 1px 0;&lt;br /&gt;
}&lt;br /&gt;
.wiki_quality td&lt;br /&gt;
{&lt;br /&gt;
  border: 1px #DDD solid !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_red&lt;br /&gt;
{&lt;br /&gt;
    background-color: #FFA288 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_yellow&lt;br /&gt;
{&lt;br /&gt;
    background-color: #FFECAD !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_green&lt;br /&gt;
{&lt;br /&gt;
    background-color: #96D1AD !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_warning_01&lt;br /&gt;
{&lt;br /&gt;
   margin: 0px 0px 7px 0px;&lt;br /&gt;
   padding: 1ex;&lt;br /&gt;
   border: 5px solid #FFE489;&lt;br /&gt;
   font-size: 95%&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_warning_02&lt;br /&gt;
{&lt;br /&gt;
   margin: 0px 0px 7px 0px;&lt;br /&gt;
   padding: 1ex;&lt;br /&gt;
   border: 5px solid #FFA288;&lt;br /&gt;
   font-size: 95%&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_highlight_01&lt;br /&gt;
{&lt;br /&gt;
   margin: 0px 0px 7px 0px;&lt;br /&gt;
   padding: 1ex;&lt;br /&gt;
   border: 5px solid #96D1AD;&lt;br /&gt;
   font-size: 95%&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wiki_disambiguation_01&lt;br /&gt;
{&lt;br /&gt;
  border: 1px solid #aaa;&lt;br /&gt;
  border-width:2px 0 2px 0;&lt;br /&gt;
  background-color: #f9f9f9;&lt;br /&gt;
  padding: 5px 5px 0px 5px;&lt;br /&gt;
  margin: 0px 0px 7px 0px;&lt;br /&gt;
  font-size:100%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.inline * {display:inline}&lt;br /&gt;
&lt;br /&gt;
.mw-codeX&lt;br /&gt;
{&lt;br /&gt;
  max-width: 48em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.formInput&lt;br /&gt;
{ &lt;br /&gt;
  width: 30em&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.formula&lt;br /&gt;
{&lt;br /&gt;
  display: block;&lt;br /&gt;
  margin:  0 0 1ex 1em;&lt;br /&gt;
  max-width: 100%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.regular&lt;br /&gt;
{&lt;br /&gt;
  font-style: regular;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.quote&lt;br /&gt;
{&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-style: italic;&lt;br /&gt;
  margin:  0 0 1ex 1em;&lt;br /&gt;
  max-width: 43em;&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.quote em, .quote i, i em, em em, em i&lt;br /&gt;
{&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.quote2&lt;br /&gt;
{&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
  margin:  0 0 0 1em;&lt;br /&gt;
  max-width: 44em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
div.floatright&lt;br /&gt;
{&lt;br /&gt;
  clear: right;&lt;br /&gt;
  float: right;&lt;br /&gt;
  margin: 0.5em 0 0.8em 1.4em;&lt;br /&gt;
  position: relative;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.floatleft&lt;br /&gt;
{&lt;br /&gt;
  clear: left;&lt;br /&gt;
  float: left;&lt;br /&gt;
  margin: 0.5em 0 0.8em 1.4em;&lt;br /&gt;
  position: relative;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.mw-code &lt;br /&gt;
{&lt;br /&gt;
  margin:  0 0 1ex 1em;&lt;br /&gt;
}&lt;br /&gt;
/* Add arrows to toggle-blocks for collapsible elements */&lt;br /&gt;
.mw-collapsible-arrowtoggle.mw-collapsible-toggle-expanded {&lt;br /&gt;
 padding-left: 20px !important;&lt;br /&gt;
 background-image: url(&#039;//upload.wikimedia.&lt;br /&gt;
org/wikipedia/commons/1/10/MediaWiki_Vector_skin_action_arrow.png&#039;);&lt;br /&gt;
 background-repeat: no-repeat;&lt;br /&gt;
 background-position: center left;&lt;br /&gt;
}&lt;br /&gt;
.mw-collapsible-arrowtoggle.mw-collapsible-toggle-collapsed {&lt;br /&gt;
 padding-left: 20px !important;&lt;br /&gt;
 background-image: url(&#039;//upload.wikimedia.org/wikipedia/commons/4/41/MediaWiki_Vector_skin_right_arrow.png&#039;);&lt;br /&gt;
 background-repeat: no-repeat;&lt;br /&gt;
 background-position: center left;&lt;br /&gt;
}&lt;br /&gt;
.action a img&lt;br /&gt;
{ margin: -5px 0 0 -16px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.bg1 { background-color: #ffd890 }&lt;br /&gt;
.bg2 { background-color: #b4fff1 }&lt;br /&gt;
.bg3 { background-color: #c6e894 }&lt;br /&gt;
.bg4 { background-color: #e89898 }&lt;br /&gt;
.bg5 { background-color: #c6acff }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Wikipedia */&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe1 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe1, table.hintergrundfarbe1, .hintergrundfarbe1 {&lt;br /&gt;
    background-color: #f9f9f9;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe2 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe2, table.hintergrundfarbe2, .hintergrundfarbe2 {&lt;br /&gt;
    background-color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe3 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe3, table.hintergrundfarbe3, .hintergrundfarbe3 {&lt;br /&gt;
    background-color: #ffff40;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe4 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe4, table.hintergrundfarbe4, .hintergrundfarbe4 {&lt;br /&gt;
    background-color: #fa0;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe5 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe5, table.hintergrundfarbe5, .hintergrundfarbe5 {&lt;br /&gt;
    background-color: #e0e0e0;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe6 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe6, table.hintergrundfarbe6, .hintergrundfarbe6 {&lt;br /&gt;
    background-color: #b3b7ff;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe7 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe7, table.hintergrundfarbe7, .hintergrundfarbe7 {&lt;br /&gt;
    background-color: #ffcbcb;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe8 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe8, table.hintergrundfarbe8, .hintergrundfarbe8 {&lt;br /&gt;
    background-color: #ffebad;&lt;br /&gt;
}&lt;br /&gt;
table &amp;gt; * &amp;gt; tr.hintergrundfarbe9 &amp;gt; th, table &amp;gt; * &amp;gt; tr &amp;gt; th.hintergrundfarbe9, table.hintergrundfarbe9, .hintergrundfarbe9 {&lt;br /&gt;
    background-color: #b9ffc5;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Studienpläne */&lt;br /&gt;
&lt;br /&gt;
.bgdgrey, .bgdgrau&lt;br /&gt;
{ background: #555; }&lt;br /&gt;
&lt;br /&gt;
.bggrey, .bggrau &lt;br /&gt;
{ background: #999; }&lt;br /&gt;
 &lt;br /&gt;
.bgorange&lt;br /&gt;
{ background: #f60; }&lt;br /&gt;
&lt;br /&gt;
.bgblue, .bgblau&lt;br /&gt;
{ background: #36c; }&lt;br /&gt;
&lt;br /&gt;
.bgred, .bgrot&lt;br /&gt;
{ background: #c03; }&lt;br /&gt;
&lt;br /&gt;
.bgdgrey a, .bgdgrau a&lt;br /&gt;
{ color:      #fff !important; }&lt;br /&gt;
&lt;br /&gt;
.bggrey a, .bggrau a&lt;br /&gt;
{ color:      #fff !important; }&lt;br /&gt;
&lt;br /&gt;
.bgorange a&lt;br /&gt;
{ color:      #fff  !important; }&lt;br /&gt;
&lt;br /&gt;
.bgblue a, .bgblau a&lt;br /&gt;
{ color:      #fff  !important; }&lt;br /&gt;
&lt;br /&gt;
.bgred a, .bgrot a&lt;br /&gt;
{ color:      #fff  !important; }&lt;br /&gt;
&lt;br /&gt;
.bgdgrey a, .bgdgrau a&lt;br /&gt;
{ color:      #fff  !important; }&lt;br /&gt;
&lt;br /&gt;
.grey, .grau  &lt;br /&gt;
{ color: #999 }&lt;br /&gt;
&lt;br /&gt;
.dgrey, .dgrau  &lt;br /&gt;
{ color: #666; }&lt;br /&gt;
&lt;br /&gt;
.orange  &lt;br /&gt;
{ color: #f60; }&lt;br /&gt;
 &lt;br /&gt;
.white &lt;br /&gt;
{ color: #fff !important; }&lt;br /&gt;
&lt;br /&gt;
.amber &lt;br /&gt;
{ color: #f60; }&lt;br /&gt;
&lt;br /&gt;
.blue, .blau &lt;br /&gt;
{ color: #36c; }&lt;br /&gt;
&lt;br /&gt;
.studienplan&lt;br /&gt;
{ border-collapse: collapse; }&lt;br /&gt;
&lt;br /&gt;
.studienplan tr &lt;br /&gt;
{ height: 1em; }&lt;br /&gt;
&lt;br /&gt;
.studienplan td &lt;br /&gt;
{ border:         1px solid #222;&lt;br /&gt;
  vertical-align: text-top;&lt;br /&gt;
  padding:        5px 5px 5px 5px; &lt;br /&gt;
  width:          auto;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.studienplan td.noborder &lt;br /&gt;
{ border: 0px; }&lt;br /&gt;
&lt;br /&gt;
.studienplan&lt;br /&gt;
{ border-collapse: collapse; }&lt;br /&gt;
&lt;br /&gt;
.studienplan &amp;gt; * &amp;gt; td &lt;br /&gt;
{ border: 1px solid #222; }&lt;br /&gt;
&lt;br /&gt;
table.wikitable&lt;br /&gt;
{ margin: 2ex 0 3ex 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.texttable&lt;br /&gt;
{ margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
  border: 0 !important; &lt;br /&gt;
  border-collapse: collapse; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.texttable td&lt;br /&gt;
{ border: 0 !important; &lt;br /&gt;
  margin: 0 !important;&lt;br /&gt;
  padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.texttable td:first-child&lt;br /&gt;
{ padding: 0 !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.texttable td:first-child ~ td&lt;br /&gt;
{ padding-left: 0.2em !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.wikitable &amp;gt; th &lt;br /&gt;
{ background-color: #ddd !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.wikitable td, table.wikitable &amp;gt; th,&lt;br /&gt;
table.wikitext td, table.wikitext &amp;gt; th&lt;br /&gt;
{ text-align: left !important;&lt;br /&gt;
  vertical-align: top !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
table.wikitable td, table.wikitable &amp;gt; th,&lt;br /&gt;
table.wikitext td, table.wikitext &amp;gt; th&lt;br /&gt;
{ text-align: left !important;&lt;br /&gt;
  vertical-align: top !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
### Tooltips&lt;br /&gt;
&lt;br /&gt;
#tooltip&lt;br /&gt;
{ font-size: 0.875em;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  text-shadow: 0 1px rgba( 0, 0, 0, .5 );&lt;br /&gt;
  line-height: 1.5;&lt;br /&gt;
  color: #fff;&lt;br /&gt;
  background: #333;&lt;br /&gt;
  background: -webkit-gradient( linear, left top, left bottom, from( rgba( 0, 0, 0, .6 ) ), to( rgba( 0, 0, 0, .8 ) ) );&lt;br /&gt;
  background: -webkit-linear-gradient( top, rgba( 0, 0, 0, .6 ), rgba( 0, 0, 0, .8 ) );&lt;br /&gt;
  background: -moz-linear-gradient( top, rgba( 0, 0, 0, .6 ), rgba( 0, 0, 0, .8 ) );&lt;br /&gt;
  background: -ms-radial-gradient( top, rgba( 0, 0, 0, .6 ), rgba( 0, 0, 0, .8 ) );&lt;br /&gt;
  background: -o-linear-gradient( top, rgba( 0, 0, 0, .6 ), rgba( 0, 0, 0, .8 ) );&lt;br /&gt;
  background: linear-gradient( top, rgba( 0, 0, 0, .6 ), rgba( 0, 0, 0, .8 ) );&lt;br /&gt;
  -webkit-border-radius: 5px;&lt;br /&gt;
  -moz-border-radius: 5px;&lt;br /&gt;
  border-radius: 5px;&lt;br /&gt;
  border-top: 1px solid #fff;&lt;br /&gt;
  -webkit-box-shadow: 0 3px 5px rgba( 0, 0, 0, .3 );&lt;br /&gt;
  -moz-box-shadow: 0 3px 5px rgba( 0, 0, 0, .3 );&lt;br /&gt;
  box-shadow: 0 3px 5px rgba( 0, 0, 0, .3 );&lt;br /&gt;
  position: absolute;&lt;br /&gt;
  z-index: 100;&lt;br /&gt;
  padding: 15px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#tooltip:after&lt;br /&gt;
{&lt;br /&gt;
  width: 0;&lt;br /&gt;
  height: 0;&lt;br /&gt;
  border-left: 10px solid transparent;&lt;br /&gt;
  border-right: 10px solid transparent;&lt;br /&gt;
  border-top: 10px solid #333;&lt;br /&gt;
  border-top-color: rgba( 0, 0, 0, .7 );&lt;br /&gt;
  content: &#039;&#039;;&lt;br /&gt;
  position: absolute;&lt;br /&gt;
  left: 50%;&lt;br /&gt;
  bottom: -10px;&lt;br /&gt;
  margin-left: -10px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#tooltip.top:after&lt;br /&gt;
{&lt;br /&gt;
  border-top-color: transparent;&lt;br /&gt;
  border-bottom: 10px solid #333;&lt;br /&gt;
  border-bottom-color: rgba( 0, 0, 0, .6 );&lt;br /&gt;
  top: -20px;&lt;br /&gt;
  bottom: auto;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#tooltip.left:after&lt;br /&gt;
{&lt;br /&gt;
  left: 10px;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#tooltip.right:after&lt;br /&gt;
{&lt;br /&gt;
  right: 10px;&lt;br /&gt;
  left: auto;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
abbr[title]&lt;br /&gt;
{&lt;br /&gt;
  border-bottom: 0 !important&lt;br /&gt;
}&lt;br /&gt;
*/&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Vorlage:HSA-Mitglied&amp;diff=53275</id>
		<title>Vorlage:HSA-Mitglied</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Vorlage:HSA-Mitglied&amp;diff=53275"/>
		<updated>2026-01-20T07:45:49Z</updated>

		<summary type="html">&lt;p&gt;Kowa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;__NOCACHE__&amp;lt;nowiki/&amp;gt;&amp;lt;!--&lt;br /&gt;
--&amp;gt;{{#arraydefine:fak|{{{fakultaet|}}}}}&amp;lt;!--&lt;br /&gt;
--&amp;gt;{{#loop: i&lt;br /&gt;
     | 0&lt;br /&gt;
     | {{#arraysize:fak}}&lt;br /&gt;
     | {{#set: HSA:Person:Fakultaet = {{#arrayindex:fak|{{#var:i}}}} }}&lt;br /&gt;
   }}&amp;lt;!--&lt;br /&gt;
--&amp;gt;{{#arraydefine:stg|{{{studiengang|}}}}}&amp;lt;!--&lt;br /&gt;
--&amp;gt;{{#loop: i&lt;br /&gt;
     | 0&lt;br /&gt;
     | {{#arraysize:stg}}&lt;br /&gt;
     | {{#set: HSA:Person:Studiengang = {{#arrayindex:stg|{{#var:i}}}} }}&lt;br /&gt;
   }}&amp;lt;!--&lt;br /&gt;
--&amp;gt;{{#set: HSA:Person:Funktion = {{{funktion|}}} }}&amp;lt;!--&lt;br /&gt;
--&amp;gt;{{#set: HSA:Person:Pruefer = {{{pruefer|}}} }}&amp;lt;!--&lt;br /&gt;
--&amp;gt;{{#arraydefine:f|{{#ask: [[{{FULLPAGENAME}}]]|?HSA:Person:Fakultaet|format=array|mainlabel=-|headers=hide|pagetitle=hide|manysep=,}} }}&amp;lt;!--&lt;br /&gt;
--&amp;gt;{{#arraydefine:s|{{#ask: [[{{FULLPAGENAME}}]]|?HSA:Person:Studiengang|format=array|mainlabel=-|headers=hide|pagetitle=hide|manysep=,}} }}&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Person:HSA-Mitglied]]&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
==Benutzung==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;nowiki&amp;gt;{{Studiengang&lt;br /&gt;
| fakultaet     = &amp;amp;lt;FAKULTÄT 1&amp;amp;gt;, &amp;amp;lt;FAKULTÄT 2&amp;amp;gt; ...&lt;br /&gt;
| studiengang   = &amp;amp;lt;STUDIENGANG 1&amp;amp;gt;, &amp;amp;lt;STUDIENGANG 2&amp;amp;gt; ...&lt;br /&gt;
| funktion      = Professor|Lehrbeauftragter|Wissenschaftlicher Mitarbeiter|Mitarbeiter&lt;br /&gt;
| pruefer       = wahr|falsch&lt;br /&gt;
}}&amp;lt;/nowiki&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Kategorie:Vorlage:HSA|HSA-Mitglied]][[Kategorie:Vorlage:SMW|HSA-Mitglied]]&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Wolfgang_Kowarschick&amp;diff=53274</id>
		<title>Wolfgang Kowarschick</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Wolfgang_Kowarschick&amp;diff=53274"/>
		<updated>2026-01-20T07:43:30Z</updated>

		<summary type="html">&lt;p&gt;Kowa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Qualität&lt;br /&gt;
|correctness=5&lt;br /&gt;
|extent=1&lt;br /&gt;
|numberOfReferences=1&lt;br /&gt;
|qualityOfReferences=1&lt;br /&gt;
|conformance=3&lt;br /&gt;
}}&lt;br /&gt;
{{Person&lt;br /&gt;
|anrede=Herr&lt;br /&gt;
|titel=Prof. Dr.&lt;br /&gt;
|titelLang=Prof. Dr. rer. nat. Dipl.-Math. (Univ.)&lt;br /&gt;
|nachname=Kowarschick&lt;br /&gt;
|vorname=Wolfgang&lt;br /&gt;
|vornamen=Wolfgang Lukas Josef&lt;br /&gt;
|name=&lt;br /&gt;
|titelNach=&lt;br /&gt;
|titelNachLang=&lt;br /&gt;
|nameLang=&lt;br /&gt;
|nameVollstaendig=&lt;br /&gt;
|nameVollstaendigLang=&lt;br /&gt;
|homepage=http://kowa.hs-augsburg.de/&lt;br /&gt;
|geburtsdatum=1961/05/05&lt;br /&gt;
|geburtsort=Selb&lt;br /&gt;
|todesort=&lt;br /&gt;
|hochschule=Hochschule Augsburg, Technische Universität München&lt;br /&gt;
|portrait=Datei:Kowacomic.jpg&lt;br /&gt;
|portraitTitel=&lt;br /&gt;
|vater=&lt;br /&gt;
|mutter=&lt;br /&gt;
}}&lt;br /&gt;
{{HSA-Mitglied&lt;br /&gt;
|fakultaet=FKI&lt;br /&gt;
|studiengang=&lt;br /&gt;
|funktion=Professor&lt;br /&gt;
}}&lt;br /&gt;
{{Autor&lt;br /&gt;
|dblp=k/Kowarschick:Wolfgang_L=_J=&lt;br /&gt;
|gnd=123628636&lt;br /&gt;
|lccn=n93016142&lt;br /&gt;
|viaf=32909050&lt;br /&gt;
}}&lt;br /&gt;
==Quellen==&lt;br /&gt;
&lt;br /&gt;
* Die Person ist dem Autor persönlich bekannt.&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Wolfgang_Kowarschick&amp;diff=53273</id>
		<title>Wolfgang Kowarschick</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Wolfgang_Kowarschick&amp;diff=53273"/>
		<updated>2026-01-20T07:42:45Z</updated>

		<summary type="html">&lt;p&gt;Kowa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Qualität&lt;br /&gt;
|correctness=5&lt;br /&gt;
|extent=1&lt;br /&gt;
|numberOfReferences=1&lt;br /&gt;
|qualityOfReferences=1&lt;br /&gt;
|conformance=3&lt;br /&gt;
}}&lt;br /&gt;
{{Person&lt;br /&gt;
|anrede=Herr&lt;br /&gt;
|titel=Prof. Dr.&lt;br /&gt;
|titelLang=Prof. Dr. rer. nat. Dipl.-Math. (Univ.)&lt;br /&gt;
|nachname=Kowarschick&lt;br /&gt;
|vorname=Wolfgang&lt;br /&gt;
|vornamen=Wolfgang Lukas Josef&lt;br /&gt;
|name=&lt;br /&gt;
|titelNach=&lt;br /&gt;
|titelNachLang=&lt;br /&gt;
|nameLang=&lt;br /&gt;
|nameVollstaendig=&lt;br /&gt;
|nameVollstaendigLang=&lt;br /&gt;
|homepage=http://kowa.hs-augsburg.de/&lt;br /&gt;
|geburtsdatum=1961/05/05&lt;br /&gt;
|geburtsort=Selb&lt;br /&gt;
|todesort=&lt;br /&gt;
|hochschule=Hochschule Augsburg, Technische Universität München&lt;br /&gt;
|portrait=Datei:Kowacomic.jpg&lt;br /&gt;
|portraitTitel=&lt;br /&gt;
|vater=&lt;br /&gt;
|mutter=&lt;br /&gt;
}}&lt;br /&gt;
{{HSA-Mitglied&lt;br /&gt;
|fakultaet=FKI&lt;br /&gt;
|studiengang=&lt;br /&gt;
|funktion=&lt;br /&gt;
|pruefer=Nein&lt;br /&gt;
}}&lt;br /&gt;
{{Autor&lt;br /&gt;
|dblp=k/Kowarschick:Wolfgang_L=_J=&lt;br /&gt;
|gnd=123628636&lt;br /&gt;
|lccn=n93016142&lt;br /&gt;
|viaf=32909050&lt;br /&gt;
}}&lt;br /&gt;
==Quellen==&lt;br /&gt;
&lt;br /&gt;
* Die Person ist dem Autor persönlich bekannt.&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Wolfgang_Kowarschick&amp;diff=53272</id>
		<title>Wolfgang Kowarschick</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Wolfgang_Kowarschick&amp;diff=53272"/>
		<updated>2026-01-20T07:41:45Z</updated>

		<summary type="html">&lt;p&gt;Kowa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Qualität&lt;br /&gt;
|correctness=5&lt;br /&gt;
|extent=1&lt;br /&gt;
|numberOfReferences=1&lt;br /&gt;
|qualityOfReferences=1&lt;br /&gt;
|conformance=3&lt;br /&gt;
}}&lt;br /&gt;
{{Person&lt;br /&gt;
|anrede=Herr&lt;br /&gt;
|titel=Prof. Dr.&lt;br /&gt;
|titelLang=Prof. Dr. rer. nat. Dipl.-Math. (Univ.)&lt;br /&gt;
|nachname=Kowarschick&lt;br /&gt;
|vorname=Wolfgang&lt;br /&gt;
|vornamen=Wolfgang Lukas Josef&lt;br /&gt;
|name=&lt;br /&gt;
|titelNach=&lt;br /&gt;
|titelNachLang=&lt;br /&gt;
|nameLang=&lt;br /&gt;
|nameVollstaendig=&lt;br /&gt;
|nameVollstaendigLang=&lt;br /&gt;
|homepage=http://kowa.hs-augsburg.de/&lt;br /&gt;
|geburtsdatum=1961/05/05&lt;br /&gt;
|geburtsort=Selb&lt;br /&gt;
|todesort=&lt;br /&gt;
|hochschule=Hochschule Augsburg, Technische Universität München&lt;br /&gt;
|portrait=Datei:Kowacomic.jpg&lt;br /&gt;
|portraitTitel=&lt;br /&gt;
|vater=&lt;br /&gt;
|mutter=&lt;br /&gt;
}}&lt;br /&gt;
{{HSA-Mitglied&lt;br /&gt;
|fakultaet=FKI&lt;br /&gt;
|studiengang=&lt;br /&gt;
|funktion=Professor&lt;br /&gt;
|pruefer=Nein&lt;br /&gt;
}}&lt;br /&gt;
{{Autor&lt;br /&gt;
|dblp=k/Kowarschick:Wolfgang_L=_J=&lt;br /&gt;
|gnd=123628636&lt;br /&gt;
|lccn=n93016142&lt;br /&gt;
|viaf=32909050&lt;br /&gt;
}}&lt;br /&gt;
==Quellen==&lt;br /&gt;
&lt;br /&gt;
* Die Person ist dem Autor persönlich bekannt.&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Wolfgang_Kowarschick&amp;diff=53271</id>
		<title>Wolfgang Kowarschick</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Wolfgang_Kowarschick&amp;diff=53271"/>
		<updated>2026-01-20T07:37:23Z</updated>

		<summary type="html">&lt;p&gt;Kowa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Qualität&lt;br /&gt;
|correctness=5&lt;br /&gt;
|extent=1&lt;br /&gt;
|numberOfReferences=1&lt;br /&gt;
|qualityOfReferences=1&lt;br /&gt;
|conformance=3&lt;br /&gt;
}}&lt;br /&gt;
{{Person&lt;br /&gt;
|anrede=Herr&lt;br /&gt;
|titel=Prof. Dr.&lt;br /&gt;
|titelLang=Prof. Dr. rer. nat. Dipl.-Math. (Univ.)&lt;br /&gt;
|nachname=Kowarschick&lt;br /&gt;
|vorname=Wolfgang&lt;br /&gt;
|vornamen=Wolfgang Lukas Josef&lt;br /&gt;
|name=&lt;br /&gt;
|titelNach=&lt;br /&gt;
|titelNachLang=&lt;br /&gt;
|nameLang=&lt;br /&gt;
|nameVollstaendig=&lt;br /&gt;
|nameVollstaendigLang=&lt;br /&gt;
|homepage=http://kowa.hs-augsburg.de/&lt;br /&gt;
|geburtsdatum=1961/05/05&lt;br /&gt;
|geburtsort=Selb&lt;br /&gt;
|todesort=&lt;br /&gt;
|hochschule=Hochschule Augsburg, Technische Universität München&lt;br /&gt;
|portrait=Datei:Kowacomic.jpg&lt;br /&gt;
|portraitTitel=&lt;br /&gt;
|vater=&lt;br /&gt;
|mutter=&lt;br /&gt;
}}&lt;br /&gt;
{{Autor&lt;br /&gt;
|dblp=k/Kowarschick:Wolfgang_L=_J=&lt;br /&gt;
|gnd=123628636&lt;br /&gt;
|lccn=n93016142&lt;br /&gt;
|viaf=32909050&lt;br /&gt;
}}&lt;br /&gt;
==Quellen==&lt;br /&gt;
&lt;br /&gt;
* Die Person ist dem Autor persönlich bekannt.&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Geordnetes_Paar:_Definition_von_Frege&amp;diff=53270</id>
		<title>Geordnetes Paar: Definition von Frege</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Geordnetes_Paar:_Definition_von_Frege&amp;diff=53270"/>
		<updated>2025-09-30T06:48:27Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Frage-Paare als JavaScript-Funktionen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Qualität&lt;br /&gt;
|correctness         = 4&lt;br /&gt;
|extent              = 4&lt;br /&gt;
|numberOfReferences  = 5&lt;br /&gt;
|qualityOfReferences = 5&lt;br /&gt;
|conformance         = 5&lt;br /&gt;
}}&lt;br /&gt;
Der Artikel „[[Geordnetes Paar]]“ befasst sich mit der Geschichte und den wichtigsten Definitionen des „geordneten Paares“.&lt;br /&gt;
Die älteste mir bekannte Definition, die den Paar-Begriff auf zuvor definierte Grundbegriffe zurückführt,&lt;br /&gt;
ist die Definition von [[Gottlob Frege]].&lt;br /&gt;
&lt;br /&gt;
Da die Symbolik, die Frege verwendet, ungewöhnlich ist &lt;br /&gt;
(dies war einer der Gründe, warum er zu Lebzeiten wenig Anerkennung fand&amp;lt;ref&amp;gt;{{Quelle|Wußing (2009)}}, S. 402&amp;lt;/ref&amp;gt;),&lt;br /&gt;
ist seine Definition nicht ohne Weiters zu verstehen. Im Folgenden soll daher versucht werden, diese Definition etwas zu veranschaulichen.&lt;br /&gt;
 &lt;br /&gt;
==[[Gottlob Frege|Frege]] (1893)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;&amp;gt;{{Quelle|Frege (1893)}}&amp;lt;/ref&amp;gt;==&lt;br /&gt;
[[Datei:Frege (1893), §144, S179.png|mini|300px|Definition des geordneten Paars, Frege (1893), §144, S. 179&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Wir definiren nun das Paar so:&#039;&#039;&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Vdash\acute{\epsilon}(o \frown (a \frown \epsilon)) = o; a&amp;lt;/math&amp;gt; &amp;lt;/div&amp;gt; &lt;br /&gt;
&#039;&#039;Das Semikolon ist hierbei zweiseitiges Functionszeichen.&#039;&#039; (Frege (1893), §144, S. 179&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
==Probleme mit dem Verständnis von Freges Definition==&lt;br /&gt;
Freges Definition ist nicht zu verstehen, wenn man seine Symbolik nicht kennt oder falsch interpretiert.&lt;br /&gt;
Zum Beispiel merkt [[Richard G. Heck Jnr.|Richard Heck]] zu dieser Definition fälschlicherweise an, dass&lt;br /&gt;
sie weder mengentheoretisch noch mit Hilfe der [[Prädikatenlogik zweiter Ordnung]] konsistent nachgebildet werden kann:&lt;br /&gt;
{{Quote|Unfortunately, Frege&#039;s definition of ordered pairs is, as George Boolos once put it, extravagant&lt;br /&gt;
and can not be consistently reconstructed, either in second-order logic or in set-theory.&amp;lt;ref&amp;gt;{{Quelle|Heck (1997)}}&amp;lt;/ref&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
Auch ist nicht notwendigerweise jeder Versuch, diese Definition in eine moderne Form zu zu bringen, von Erfolg gekrönt.&lt;br /&gt;
Der [[C.S. Peirce]] Professor of American Philosophy [[Randall Dipert]] übersetzt Freges Definition beispielsweise folgendermaßen:&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;&amp;lt;x,y&amp;gt; := \{x,\{y\}\}&amp;lt;/math&amp;gt;&amp;lt;ref name=&amp;quot;Dipert&amp;quot;&amp;gt;{{Quelle|Dipert (1982)}}, S. 355&amp;lt;/ref&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
Er merkt an, dass diese Definition falsch ist, weil z.B. die Paare &amp;lt;math&amp;gt;&amp;lt;\{\{c\}\}, c&amp;gt;&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;&amp;lt;\{c\}, \{c\}&amp;gt;&amp;lt;/math&amp;gt; nicht unterschieden werden könnten,&lt;br /&gt;
da sie beide durch &amp;lt;math&amp;gt;\{\{\{c\}\}, \{c\}\}&amp;lt;/math&amp;gt; repräsentiert würden. Das stimmt zwar, ist aber nicht Freges Definition anzulasten,&lt;br /&gt;
sondern Diperts falscher Interpretation dieser Definition.&lt;br /&gt;
&lt;br /&gt;
Gottlob Frege definiert das Paar &amp;lt;math&amp;gt;o;a&amp;lt;/math&amp;gt; mit Hilfe eines „Werthverlaufs“ &amp;lt;math&amp;gt;\acute{\epsilon}\phi(\epsilon)&amp;lt;/math&amp;gt;&lt;br /&gt;
und der „Function &amp;lt;math&amp;gt;\xi \frown \zeta&amp;lt;/math&amp;gt; mit zwei Argumenten“ (Frege (1893, §9 und §34)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;). Da Frege der Funktion &amp;lt;math&amp;gt;\frown&amp;lt;/math&amp;gt;&lt;br /&gt;
keinen Namen gibt, soll diese im Folgenden „Frown“ heißen (in Anlehnung an den Namen des [[LaTeX]]-Symbols, mit dem sie in diesem Beitrag dargestellt wird). &lt;br /&gt;
&lt;br /&gt;
Um die Definition von Frege zu verstehen, muss man die Bedeutung des Wertverlaufs einer Funktion und die Frown-Funktion kennen.&lt;br /&gt;
Auch hier leitet die Literatur einen leicht in die Irre. Beispielsweise interpretiert Edward Zalta (2014, Abschnitt 2.3) die Frown-Operation &lt;br /&gt;
als Element-Operation &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt;.&amp;lt;ref name=&amp;quot;Zalta (2014)&amp;quot;&amp;gt;{{Quelle|Zalta (2014)}}&amp;lt;/ref&amp;gt;&lt;br /&gt;
Das ist richtig, falls der Wertverlauf einer Prädikatsfunktion betrachtet wird, d.h. einer Funktion, die jedes Element des Frege-Universums auf einen [[Wahheitswert]] abbildet. &lt;br /&gt;
Eine derartige Funktion wird von Frege „Begriff“ genannt (Zalta verwendet die englische Übersetzung “concept”). Die Frown-Funktion von Frege ist&lt;br /&gt;
allerdings nicht nur für Wertverläufe von „Begriffen“ definiert, sondern für Wertverläufe von beliebigen Funktionen. Das heißt, i. Allg. kann man &amp;lt;math&amp;gt;\frown&amp;lt;/math&amp;gt; nicht mit &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; gleichsetzen.&lt;br /&gt;
&lt;br /&gt;
==Wertverlauf und Frown-Funktion==&lt;br /&gt;
Freges Definition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Vdash\acute{\epsilon}(o \frown (a \frown \epsilon)) = o;a&amp;lt;/math&amp;gt; &amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
enthält drei unübliche Symbole, deren Bedeutungen im Folgenden näher beleuchtet werden: &amp;lt;math&amp;gt;\Vdash&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\acute\cdot&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\frown&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Der Definitionsdoppelstrich===&lt;br /&gt;
&amp;lt;math&amp;gt;\Vdash&amp;lt;/math&amp;gt; ist der „Definitionsdoppelstrich“, der von Frege als Erweiterung des „Urtheilstrichs“ &amp;lt;math&amp;gt;\vdash&amp;lt;/math&amp;gt;&lt;br /&gt;
definiert wird (Frege (1893, §27 und §5)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;). Das Symbol &amp;lt;math&amp;gt;\vdash&amp;lt;/math&amp;gt; wird in der Logik heute noch als  „Herleitungs“-, „Ableitungs“- oder „[[Inferenzoperator]]“ verwendet. &lt;br /&gt;
&lt;br /&gt;
Der Definitionsdoppelstrich sagt zusammen mit dem Gleichheitszeichen &amp;lt;math&amp;gt;=&amp;lt;/math&amp;gt; aus,&lt;br /&gt;
dass der Term &amp;lt;math&amp;gt;o;a&amp;lt;/math&amp;gt; eine abkürzende Schreibweise für den Term &amp;lt;math&amp;gt;\acute{\epsilon}(o \frown (a \frown \epsilon))&amp;lt;/math&amp;gt; ist.&lt;br /&gt;
Heutzutage würde man üblicherweise folgende Schreibweise verwenden:&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;o;a := \acute{\epsilon}(o \frown (a \frown \epsilon)) &amp;lt;/math&amp;gt; &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Der Wertverlaufsoperator===&lt;br /&gt;
Der Operator &amp;lt;math&amp;gt;\acute\cdot&amp;lt;/math&amp;gt; wird von Frege genutzt, um den „Werthverlauf“ einer Funktion zu beschreiben (Frege (1893), §9&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;).&lt;br /&gt;
Laut Kutschera (1989, S. 96)&amp;lt;ref name = &amp;quot;Kutschera&amp;quot;&amp;gt;{{Quelle|Kutschera (1989)}}&amp;lt;/ref&amp;gt; kann man den&lt;br /&gt;
Wertverlauf &amp;lt;math&amp;gt;\acute{\epsilon}\phi(\epsilon)&amp;lt;/math&amp;gt; einer Funktion &amp;lt;math&amp;gt;\phi&amp;lt;/math&amp;gt; in heutigen Klassensystemen &lt;br /&gt;
durch die Klasse &amp;lt;math&amp;gt;\{[\epsilon,\phi(\epsilon)]: \top\}&amp;lt;/math&amp;gt; ausdrücken,&lt;br /&gt;
wobei die Objekte &amp;lt;math&amp;gt;[x,y]&amp;lt;/math&amp;gt; keine Frege-Paare sind, sondern geordnete Paare des zugrundeliegende Klassensystems &lt;br /&gt;
(Wiener-/Kuratowski-/Quine-/Morse-/Schmidt-Paare o. Ä.; siehe Artikel „[[geordnetes Paar]]“). &lt;br /&gt;
&lt;br /&gt;
Für Frege sind Funktionen allerdings grundlegender als Klassen. &lt;br /&gt;
In seinem Axiomensstem sind Funktionen (und deren Wertverläufe) wie beim [[Lambda-Kalkül]] von [[Alonzo Church]] Grundbegriffe, &lt;br /&gt;
die nicht definiert, sondern nur erläutert werden können&amp;lt;ref name = &amp;quot;Kutschera&amp;quot;/&amp;gt;&lt;br /&gt;
und mit deren Hilfe andere Gundbegriffe wie Klassen, Paare, natürliche Zahlen etc. definiert werden. &lt;br /&gt;
&lt;br /&gt;
Denselben Weg schlägt beispielsweise auch John von Neumann (1925) ein.&amp;lt;ref name = &amp;quot;Neumann (1925)&amp;quot;&amp;gt;{{Quelle|Neumann (1925)}}&amp;lt;/ref&amp;gt; &lt;br /&gt;
Allerdings unterscheidet sich sein Axiomensystem&lt;br /&gt;
hinsichtlich der Definition des geordneten Paares in&lt;br /&gt;
zwei wesentlichen Punkten von Freges Axiomensystem:&lt;br /&gt;
&lt;br /&gt;
* Es erlaubt nicht die uneingeschränkte Bildung von Funktionen und geordneten Paaren, so dass die [[Russellsche Antinomie]] mit großer Wahrscheinlichkeit, aber – aufgrund des zweiten [[Gödelscher Unvollständigkeitssatz|Gödelschen Unvollständigkeitssatzes]] – nicht beweisbar vermieden wird.&lt;br /&gt;
* Geordnete Paare werden axiomatisch eingeführt und nicht per Definition.&lt;br /&gt;
&lt;br /&gt;
Überlicherweise werden heute allerdings meist mengen- oder klassenbasierte Systeme (wie z.B. [[Zermelo-Fraenkel-Mengenlehre]], [[Neumann-Bernays-Gödel-Mengenlehre]], [[Quine-Rosser-Mengenlehre]]) zur Formalisierung der Mathematik eingesetzt.&lt;br /&gt;
In diesen Systemen wird zwischen einer Funktion und ihrem Wertverlauf i. Allg. nicht mehr unterschieden. Im Gegenteil: Funktionen werden&lt;br /&gt;
mit Hilfe von Wertverlaufsklassen definiert (siehe z.B. Schmidt (1966)&amp;lt;ref name=&amp;quot;Schmidt (1966)&amp;quot;&amp;gt;{{Quelle|Schmidt (1966)}}&amp;lt;/ref&amp;gt;): &lt;br /&gt;
Eine Funktion &amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; ist eine Klasse von Paaren, wobei es keine zwei Paare &amp;lt;math&amp;gt;[x, y_1], [x, y_2] \in f&amp;lt;/math&amp;gt; gibt, für die&lt;br /&gt;
&amp;lt;math&amp;gt;y_1 \not= y_2&amp;lt;/math&amp;gt; gilt.&lt;br /&gt;
&lt;br /&gt;
Da Funktionen (genauer: Wertverläufe von Funktionen) wesentlich für die Definition des Freges-Paares sind, können Funktionen nicht mit Hilfe des Frege-Paares&lt;br /&gt;
definiert werden. Dies war aber auch nicht die Absicht von Frege. Er benötigte die Paare zur&lt;br /&gt;
Konstruktion von [[reele Zahl|reellen Zahlen]] (vgl. Frege (1903, §161 ff.)&amp;lt;ref&amp;gt;{{Quelle|Frege (1903)}}&amp;lt;/ref&amp;gt;, Kutschera (1989, S.&amp;amp;nbsp;125)&amp;lt;ref name=&amp;quot;Kutschera&amp;quot;/&amp;gt;). &lt;br /&gt;
&lt;br /&gt;
Um sich Frege-Paare zu veranschaulichen, kann man dennoch einen Trick anwenden: Man identifiziert Funktionen mit ihrem Wertverlauf (so wie das in Klassensystemen üblich ist). Das einzige Problem, das man sich dabei einhandelt, ist, dass Frege-Paare &amp;lt;math&amp;gt;a;b&amp;lt;/math&amp;gt; dann – sofern man eine moderne klassenbasierten Theorie zu Hilfe nimmt – mit Hilfe von anderen Paaren &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt; definiert werden. Das ist aber nichts Ungewöhnliches. So werden z.B. auch [[Morse-Paar]]e&amp;lt;ref name=&amp;quot;Morse&amp;quot;&amp;gt;{{Quelle|Morse (1965)}}&amp;lt;/ref&amp;gt; mit Hilfe von &lt;br /&gt;
[[Kuratowski-Paar]]en&amp;lt;ref name =&amp;quot;Kuratowski&amp;quot;&amp;gt;{{Quelle|Kuratowski (1921)}}&amp;lt;/ref&amp;gt; definiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man sich den Wertverlauf-Operator als [[Lambda-Kalkül|Lambda-Operator]] vorstellen (vgl. Zalta (2014)&amp;lt;ref name=&amp;quot;Zalta (2014)&amp;quot;/&amp;gt;): &lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\acute{\epsilon}\phi(\epsilon) \;\hat=\; \lambda \epsilon .\phi(\epsilon)&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man die [[Eta-Konversion|η-Konversion]] im Lambda-Kalkül zulässt, sieht man auch hier, dass der Wertverlauf von &amp;lt;math&amp;gt;\phi&amp;lt;/math&amp;gt; mit&lt;br /&gt;
&amp;lt;math&amp;gt;\phi&amp;lt;/math&amp;gt; selbst identifiziert werden kann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\lambda \epsilon .\phi(\epsilon) \;=\; \phi&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Frege betrachtet nicht nur arithmetische Funktionen, sondern auch [[Prädikatsfunktion]]en. &lt;br /&gt;
Diese heißen bei ihm „Begriffe“ (Frege (1893, §3, S. 8)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;).&lt;br /&gt;
Eine Pädikatsfunktion liefert für alle Argumente einen [[Junktor#Nullstellige_Junktoren|Wahrheitswert]] {{Mono|T}} oder {{Mono|F}}&lt;br /&gt;
als Ergebnis. Frege bezeichnet diese Werte als „das Wahre“ und „das Falsche“.&lt;br /&gt;
Wenn &amp;lt;math&amp;gt;\phi&amp;lt;/math&amp;gt; eine Prädikatsfunktion ist, die &#039;&#039;&#039;jedem&#039;&#039;&#039; Objekt im Frege-Universum einen Wahrheitswert zuordnet,&lt;br /&gt;
kann der Wertverlauf von &amp;lt;math&amp;gt;\phi&amp;lt;/math&amp;gt; auch als Klasse aufgefasst werden,&lt;br /&gt;
die alle Elemente (Frege: „Gegenstände“) &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; enthält, für die &amp;lt;math&amp;gt;\phi(\epsilon)&amp;lt;/math&amp;gt; wahr ist.&lt;br /&gt;
Der Wertvelauf &amp;lt;math&amp;gt;\acute{\epsilon}\phi(\epsilon)&amp;lt;/math&amp;gt; entspricht unter dieser Sichtweise der Klasse &amp;lt;math&amp;gt;\{\epsilon: \phi(\epsilon)\}&amp;lt;/math&amp;gt; (vgl. Kutschera (1989, Kapitel 6.4)&amp;lt;ref name = &amp;quot;Kutschera&amp;quot;/&amp;gt;). &lt;br /&gt;
Insbesondere definiert Frege die leere Menge und einelementige Mengen mit Hilfe von Wertverläufen (führt dafür aber keine eigenen Symbole ein):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
!             || moderne Definition            || Freges Definition || Quellen&lt;br /&gt;
|- &lt;br /&gt;
| leere Menge || &amp;lt;math&amp;gt;\emptyset := \{x: x\not=x\}&amp;lt;/math&amp;gt; || &amp;lt;math&amp;gt;\acute{\epsilon}\neg(\epsilon=\epsilon)&amp;lt;/math&amp;gt; || [[Klasse_(Mengenlehre)#Spezielle_Klassen_Anlehnung_Schmidt.5B2.5D|Klasse]], Frege (1893, S. 53, vorletzter Absatz)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Einermenge || &amp;lt;math&amp;gt;\{a\} := \{x: x=a\}&amp;lt;/math&amp;gt; oder&amp;lt;br/&amp;gt;&amp;lt;math&amp;gt;\{a\} := \{x: \rm{Mg}(a) \rightarrow x = a\}&amp;lt;/math&amp;gt; || &amp;lt;math&amp;gt;\acute{\epsilon}(\epsilon=a)&amp;lt;/math&amp;gt; || [[Klasse_(Mengenlehre)#Spezielle_Klassen_Anlehnung_Schmidt.5B2.5D|Klasse]], Frege (1893, §11)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| Extraktion des Elements&amp;lt;br/&amp;gt;einer Einermenge || Es sei &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; eine Menge.&amp;lt;br/&amp;gt;&amp;lt;math&amp;gt;A =\{a\} \rightarrow \bigcup{A} = a&amp;lt;/math&amp;gt; ||Wenn &amp;lt;math&amp;gt;A =\acute{\epsilon}(\epsilon=a)&amp;lt;/math&amp;gt;, dann &amp;lt;math&amp;gt;\backslash A = a&amp;lt;/math&amp;gt; || &lt;br /&gt;
Schmidt (1966, S72, 8.28)&amp;lt;ref name=&amp;quot;Schmidt (1966)&amp;quot;/&amp;gt;, Frege (1893, §11)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Für alle Objekte &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt;, die keine Einermenge sind, setzt Frege fest, dass &amp;lt;math&amp;gt;\backslash A = A&amp;lt;/math&amp;gt; gilt.&lt;br /&gt;
Das heißt, für die leere Menge liefern &amp;lt;math&amp;gt;\bigcup&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\backslash&amp;lt;/math&amp;gt; ebenfalls dasselbe Ergebnis (nämlich die leere Menge).&lt;br /&gt;
Für alle anderen Objekte &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; unterscheiden sich die Ergebnisse von &amp;lt;math&amp;gt;\bigcup A&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\backslash A&amp;lt;/math&amp;gt; dagegen. Dies ist für&lt;br /&gt;
die Veranschaulichung der Frown-Funktion, die im nachfolgenden Abschnitt beschrieben wird, jedoch nicht von Belang,&lt;br /&gt;
da bei der Definition dieser Funktion nur die leere Menge und einelementige Mengen benötigt werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Frege_1893_§34_S53.png|mini|300px|Definition der Frown-Funktion, Frege (1893, §34, S.&amp;amp;nbsp;53)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
===Die Frown-Funktion===&lt;br /&gt;
&lt;br /&gt;
Frege definiert die Frown-Funktion folgendermaßen (Frege (1893, §34, S. 53)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Vdash\backslash\acute{\alpha}(\neg\bigwedge g : u = \acute{\epsilon}g(\epsilon) \rightarrow \neg (g(a) = \alpha)) = a \frown u&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
Dies kann man mit Hilfe der Operatoren &amp;lt;math&amp;gt;\bigvee&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; (die Frege allerdings nicht explizit definiert und daher auch nicht explitizt verwendet)&lt;br /&gt;
etwas einfacher ausdrücken, da &amp;lt;math&amp;gt;\neg \bigwedge g: a \rightarrow \neg b&amp;lt;/math&amp;gt; äquivalent zu &amp;lt;math&amp;gt;\bigvee g: a \wedge b &amp;lt;/math&amp;gt; ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Vdash\backslash\acute{\alpha}(\bigvee g : u = \acute{\epsilon}g(\epsilon) \wedge g(a) = \alpha) = a \frown u&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einem modernen Klassensystem kann man diese Definition – da es sich bei &amp;lt;math&amp;gt;\bigvee g : \ldots&amp;lt;/math&amp;gt; um eine Prädikatsfunktion handelt und es höchstens ein Element &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; gibt, das dieses Prädikat erfüllt, da also &amp;lt;math&amp;gt;\backslash A&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\bigcup A&amp;lt;/math&amp;gt; dasselbe Ergebnis liefern – folgendermaßen formulieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;a \frown u := \bigcup\{\alpha : \bigvee g : u = \acute{\epsilon}g(\epsilon) \wedge g(a) = \alpha\}&amp;lt;/math&amp;gt;&lt;br /&gt;
(der Wertlauf &amp;lt;math&amp;gt;\acute{\epsilon}g(\epsilon)&amp;lt;/math&amp;gt; repräsentiert hier natürlich die Klasse &amp;lt;math&amp;gt;\{[\epsilon,y]: y=g(\epsilon)\}&amp;lt;/math&amp;gt;)&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zwei Fälle sind zu unterscheiden: Entweder ist &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; der Wertverlauf einer Funktion g oder &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; ist dies nicht.&lt;br /&gt;
Im ersten Fall gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\vdash a \frown \acute{\epsilon}g(\epsilon) = \bigcup\{\alpha : g(a) = \alpha\} = g(a)&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Frege beweist auf den Seiten 74 und 75 denselben Satz in folgender Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\vdash f(a) = a \frown \acute{\epsilon}f(\epsilon)&amp;lt;/math&amp;gt; (Dieser Satz erhält von Frege die Satznummer 1.)&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im zweiten Fall gilt:&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\vdash a \frown u = \bigcup\{\alpha : \bigvee g : u = \acute{\epsilon}g(\epsilon) \wedge g(a) = \alpha\} = \bigcup\{\alpha : \bot\} = \emptyset&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;a \frown u&amp;lt;/math&amp;gt; liefert also, falls u der Wertverlauf einer Funktion &amp;lt;math&amp;gt;g&amp;lt;/math&amp;gt; ist, den Wert der Funktion &amp;lt;math&amp;gt;g&amp;lt;/math&amp;gt; für das Argument &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; als Ergebnis.&lt;br /&gt;
Falls &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; kein Wertverlauf ist, hat &amp;lt;math&amp;gt;a \frown u&amp;lt;/math&amp;gt; dagegen die leere Menge als Ergebnis. (vgl. Frege (1893, S. 53, letzter Absatz)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;; Kutschera (1989, S. 104)&amp;lt;ref name = &amp;quot;Kutschera&amp;quot;/&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Im Falle einer Prädikatsfunktion entspricht der &amp;lt;math&amp;gt;\frown&amp;lt;/math&amp;gt;-Operator dem in Klassensystem üblichen Elementoperator &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;a \frown \acute{\epsilon}g(\epsilon) = g(a)&amp;lt;/math&amp;gt; entspricht in diesem Fall folgender Beziehung &lt;br /&gt;
                     &amp;lt;math&amp;gt;a \in \{\epsilon: g(\epsilon)\} \leftrightarrow g(a)&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist der Spezialfall, mit dem sich Zalta näher befasst&amp;lt;ref name=&amp;quot;Zalta (2014)&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Frege 1893 S75.png|mini|300px|Satz 1 und Satz 2: Der Wertverlauf von ein- und zweistelligen Funktionen, Frege (1893), S. 75&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;]]&lt;br /&gt;
Frege zeigt auf S. 75 außerdem noch, dass sein Satz 1 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\vdash f(a) = a \frown \acute{\epsilon}f(\epsilon)&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auch für zweistellige Funktionen verallgemeinert werden kann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\vdash f(a,b) = a \frown (b \frown \acute{\alpha}\acute{\epsilon}f(\epsilon,\alpha))&amp;lt;/math&amp;gt; (Dieser Satz erhält von Frege die Satznummer 2.)&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analog zu Frege kann man auch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\vdash f(a)(b) = a \frown (b \frown \acute{\alpha}\acute{\epsilon}f(\epsilon)(\alpha))&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
zeigen. Das heißt, &amp;lt;math&amp;gt;f(a,b)&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;f(a)(b)&amp;lt;/math&amp;gt; unterscheiden sich im Prinzip nicht. Obwohl Frege diese alternative &lt;br /&gt;
Notation für Funktionen mit zwei Argumenten nicht verwendet hat, wird dsie im nächsten Abschnitt zur&lt;br /&gt;
Veranschaulichung der Paardefinition verwendet, um die Definitionen etwas schlanker zu halten.&lt;br /&gt;
&lt;br /&gt;
Anmerkung: Auf dieselbe Weise könnte man auch den Wertverlauf von beliebigen n-stelligen Funktionen definieren. Dies hat Frege allerdings nicht getan.&lt;br /&gt;
&lt;br /&gt;
===„Übersetzung“ von Freges Definition in „moderne“ Symbolik===&lt;br /&gt;
&lt;br /&gt;
Aus den vorangegangenen Überlegungen folgt, dass man in modernen Klassensystemen Freges Paardefinition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Vdash\acute{\epsilon}(o \frown (a \frown \epsilon)) = o; a&amp;lt;/math&amp;gt; &amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
mit Hilfe des [[Lambda-Kalkül]]s (das dem Axiomensystem von Frege gerade hinsichtlich des Wertverlauf-Operators etwas näher steht als ein Klassensystem) folgendermaßen formulieren kann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;o;a \,:= \; \lambda \epsilon . \epsilon a o&amp;lt;/math&amp;gt; &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für die „Übersetzung“ von Freges Definition in eine klassenbasierte Definition werden folgende Vereinbarungen getroffen.&lt;br /&gt;
&lt;br /&gt;
* Eine Funktion &amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; ist eine Klasse von Paaren mit &amp;lt;math&amp;gt;[x,y_1], [x,y_2]\in f \rightarrow y_1=y_2&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Der Funktionsaufruf-Operator &amp;lt;math&amp;gt;\cdot(\cdot)&amp;lt;/math&amp;gt; ist folgendermaßen definiert &amp;lt;math&amp;gt;f(x) := \bigcap\{y: [x,y] \in f \}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Damit definiert man:&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;o; a := \{ [\epsilon, \epsilon(a)(o)] : \top\}&amp;lt;/math&amp;gt;, d.h, &amp;lt;math&amp;gt;o; a&amp;lt;/math&amp;gt; ist eine Funktion mit &amp;lt;math&amp;gt;(o; a)(\epsilon) = \epsilon(a)(o)&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sofern &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; eine Funktion ist, für die der zweistellige Funktionsaufruf &amp;lt;math&amp;gt;\epsilon(a)(o)&amp;lt;/math&amp;gt; definiert ist, stimmen Freges Definition und die &lt;br /&gt;
Alternative-Definition überein. Für andere Funktionen und Nicht-Funktionen gilt dagegen bei Frege stets &amp;lt;math&amp;gt;(o;a)(\epsilon) = \emptyset&amp;lt;/math&amp;gt;.&lt;br /&gt;
Dies ist bei der Alternativdefinition nicht der Fall.&lt;br /&gt;
Allerdings ist es für die weiteren Betrachtungen nicht wichtig, welchen Wert &amp;lt;math&amp;gt;(o;a)(\epsilon)&amp;lt;/math&amp;gt; annimmt, falls &amp;lt;math&amp;gt;\epsilon(a)(o)&amp;lt;/math&amp;gt; nicht definiert ist.&lt;br /&gt;
&lt;br /&gt;
==Das Paaraxiom==&lt;br /&gt;
&lt;br /&gt;
[[Datei:Frege 1893 S195.png|mini|300px|Beweis des Paaraxioms, Teil 1, Frege (1893, S.&amp;amp;nbsp;195)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;]]&lt;br /&gt;
[[Datei:Frege 1893 S185.png|mini|300px|Beweis des Paaraxioms, Teil 2, Frege (1893, S.&amp;amp;nbsp;185)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;]]&lt;br /&gt;
Frege beweist die Gültigkeit des [[Paaraxiom]]s direkt, {{dh}} ohne Hilfe von Projektionsoperatoren und des Satzes &lt;br /&gt;
[[Satz:Aus der Existenz zweier Projektionsoperatoren für geordnete Paare folgt das Paaraxiom|„Aus der Existenz zweier Projektionsoperatoren für geordnete Paare folgt das Paaraxiom“]]:&lt;br /&gt;
&lt;br /&gt;
{{TBD}}&lt;br /&gt;
&lt;br /&gt;
===Die Projektionsoperatoren===&lt;br /&gt;
&lt;br /&gt;
Frege gibt für seine Paardefinition keine [[Projektionsoperatoren]] an. Es ist aber kein Problem, diese zu definieren.&lt;br /&gt;
Und damit ist ebenfalls sichergestellt, &lt;br /&gt;
[[Satz:Aus der Existenz zweier Projektionsoperatoren für geordnete Paare folgt das Paaraxiom|dass das Paaraxiom erfüllt ist]].&lt;br /&gt;
&lt;br /&gt;
Die Definition der Projektionsoperatoren kann in modernen Axiomensystemen auf (mindestens) zwei Arten erfolgen:&lt;br /&gt;
&lt;br /&gt;
# Mit Funktionen eines Mengen- oder Klassensystems.&lt;br /&gt;
# Mit Hilfe des Lambda-Kalküls.&lt;br /&gt;
&lt;br /&gt;
Man stellt fest, dass im zweiten Fall die Projektionsoperatoren wesentlich prägnanter formuliert werden können.&lt;br /&gt;
Das liegt daran, dass es sich sowohl beim Lambda-Kalkül, als auch beim Axiomensystem von Frege um funktionale Axiomensysteme handelt.&lt;br /&gt;
&lt;br /&gt;
====Die Projektionsoperatoren auf Basis von Funktionen eines Mengen- oder Klassensystems====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\begin{array}{lclclclclclcl}&lt;br /&gt;
  \rm{id}       &amp;amp; := &amp;amp; \{[x,x] : \top \}            &amp;amp; \rm{d.h.:} &amp;amp; \rm{id}(x)      &amp;amp; = &amp;amp; x                       \\&lt;br /&gt;
  \rm{const_c}  &amp;amp; := &amp;amp; \{[x,c] : \top \}            &amp;amp; \rm{d.h.:} &amp;amp; \rm{const_c}(x) &amp;amp; = &amp;amp; c                       \\&lt;br /&gt;
  \pi&#039;_1        &amp;amp; := &amp;amp; \{[x,\rm{id}] : \top \}      &amp;amp; \rm{d.h.:} &amp;amp; \pi&#039;_1(x)       &amp;amp; = &amp;amp; \rm{id}                 \\      &lt;br /&gt;
                &amp;amp;    &amp;amp;                              &amp;amp; \rm{d.h.:} &amp;amp; \pi&#039;_1(x)(y)    &amp;amp; = &amp;amp; \rm{id}(y) =  y         \\&lt;br /&gt;
  \pi&#039;_2        &amp;amp; := &amp;amp; \{[x,\rm{const}_x] : \top \} &amp;amp; \rm{d.h.:} &amp;amp; \pi&#039;_2(x)       &amp;amp; = &amp;amp; \rm{const}_x            \\&lt;br /&gt;
                &amp;amp;    &amp;amp;                              &amp;amp; \rm{d.h.:} &amp;amp; \pi&#039;_2(x)(y)    &amp;amp; = &amp;amp; \rm{const}_x(y) = x     \\&lt;br /&gt;
  \pi_1         &amp;amp; := &amp;amp; \{ [p, p(\pi&#039;_1)] : \top\}   &amp;amp; \rm{d.h.:} &amp;amp; \pi_1(p)        &amp;amp; = &amp;amp; p(\pi&#039;_1)               \\&lt;br /&gt;
  \pi_2         &amp;amp; := &amp;amp; \{ [p, p(\pi&#039;_2)] : \top\}   &amp;amp; \rm{d.h.:} &amp;amp; \pi_2(p)        &amp;amp; = &amp;amp; p(\pi&#039;_2)&lt;br /&gt;
\end{array}&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\pi_1&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\pi_2&amp;lt;/math&amp;gt; sind die gesuchten Projektionsoperatoren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\begin{array}{lcll} &lt;br /&gt;
  \pi_1(a;b) &amp;amp; = &amp;amp; (a;b) (\pi&#039;_1) &amp;amp; (\rm{Def. von}\;\pi_1) \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; \pi&#039;_1(b)(a)   &amp;amp; (\rm{Def. von}\;a;b)   \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; \rm{id}(a)     &amp;amp; (\rm{Def. von}\;\pi&#039;_1)\\ &lt;br /&gt;
             &amp;amp; = &amp;amp; a              &amp;amp; (\rm{Def. von}\;\rm{id})&lt;br /&gt;
\\ &lt;br /&gt;
  \pi_2(a;b) &amp;amp; = &amp;amp; (a;b) (\pi&#039;_2) &amp;amp; (\rm{Def. von}\;\pi_2) \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; \pi&#039;_2(b)(a)   &amp;amp; (\rm{Def. von}\;a;b)   \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; \rm{const}_b(a)&amp;amp; (\rm{Def. von}\;\pi&#039;_2)\\ &lt;br /&gt;
             &amp;amp; = &amp;amp; b              &amp;amp; (\rm{Def. von}\;\rm{const}_b)&lt;br /&gt;
\end{array}&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Die Projektionsoperatoren auf Basis des Lambda-Kalküls====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\begin{array}{lclclcl} &lt;br /&gt;
  \pi_1 &amp;amp; := &amp;amp; \lambda p. p(\lambda x.\lambda y.y) \\ &lt;br /&gt;
  \pi_2 &amp;amp; := &amp;amp; \lambda p. p(\lambda x.\lambda y.x)&lt;br /&gt;
\end{array}&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\pi_1&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\pi_2&amp;lt;/math&amp;gt; sind die gesuchten Projektionsoperatoren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\begin{array}{lcll} &lt;br /&gt;
  \pi_1(a;b) &amp;amp; = &amp;amp; (a;b) (\lambda x.\lambda y.y)                                    &amp;amp; (\rm{Def. von}\;\pi_1)    \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; (\lambda \epsilon . \epsilon b  a)(\lambda x. \lambda y. y)      &amp;amp; (\rm{Def. von}\;a;b)      \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; (\lambda x. \lambda y. y) b a                                    &amp;amp; (\rm{\beta{-}Konversion}) \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; (\lambda y. y) a                                                 &amp;amp; (\rm{\beta{-}Konversion}) \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; a                                                                &amp;amp; (\rm{\beta{-}Konversion})&lt;br /&gt;
\\ &lt;br /&gt;
  \pi_2(a;b) &amp;amp; = &amp;amp; (a;b) (\lambda x.\lambda y.x)                                    &amp;amp; (\rm{Def. von}\;\pi_2)    \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; (\lambda \epsilon . \epsilon  b  a)(\lambda x. \lambda y. x)     &amp;amp; (\rm{Def. von}\;a;b)      \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; (\lambda x. \lambda y. x) b a                                    &amp;amp; (\rm{\beta{-}Konversion}) \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; (\lambda y. b) a                                                 &amp;amp; (\rm{\beta{-}Konversion}) \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; b                                                                &amp;amp; (\rm{\beta{-}Konversion}) \\ &lt;br /&gt;
\end{array} &amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====„Rückübersetzung“ der Projektionsoperatoren in Freges Notation====&lt;br /&gt;
&lt;br /&gt;
Die Projektionsoperatoren des vorangegangenen Abschnitts können ziemlich einfach in&lt;br /&gt;
Freges Notation übersetzt werden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\begin{array}{lclclcl} &lt;br /&gt;
  \Vdash \acute{p}(p(\acute{x}(\acute{y}y))) = \pi_1 \\&lt;br /&gt;
  \Vdash \acute{p}(p(\acute{x}(\acute{y}x))) = \pi_2 \\&lt;br /&gt;
\end{array}&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\pi_1&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\pi_2&amp;lt;/math&amp;gt; sind die gesuchten Projektionsoperatoren. Der Beweis des Satzes basiert im Wesentlichen&lt;br /&gt;
auf Satz 1 von Frege: &amp;lt;math&amp;gt;\vdash f(a) = a \frown \acute{\epsilon}f(\epsilon)&amp;lt;/math&amp;gt; (§54, S. 75&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\begin{array}{lcll} &lt;br /&gt;
  \vdash \pi_1(a;b) &amp;amp; = &amp;amp; \acute{p}(p(\acute{x}(\acute{y}y)))(a;b)          &amp;amp; (\rm{Def. von}\;\pi_1)    \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; (a;b) \frown \acute{p}(p(\acute{x}(\acute{y}y)))  &amp;amp; (\rm{Frege: Satz 1})      \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; (a;b)(\acute{x}(\acute{y}y))                      &amp;amp; (\rm{Frege: Satz 1})      \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; \acute{x}(\acute{y}y) \frown (a;b)                &amp;amp; (\rm{Frege: Satz 1})      \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; \acute{x}(\acute{y}y) \frown (\acute{\epsilon}(a \frown (b \frown \epsilon)) &amp;amp; (\rm{Def. von}\;a;b) \\&lt;br /&gt;
                    &amp;amp; = &amp;amp; a \frown (b \frown \acute{x}(\acute{y}y))         &amp;amp; (\rm{Frege: Satz 1})      \\&lt;br /&gt;
                    &amp;amp; = &amp;amp; a \frown \acute{y}y                               &amp;amp; (\rm{Frege: Satz 1})      \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; a                                                 &amp;amp; (\rm{Frege: Satz 1})     &lt;br /&gt;
\\ &lt;br /&gt;
  \vdash \pi_2(a;b) &amp;amp; = &amp;amp; \acute{p}(p(\acute{x}(\acute{y}x)))(a;b)          &amp;amp; (\rm{Def. von}\;\pi_2)    \\&lt;br /&gt;
                    &amp;amp; = &amp;amp; (a;b) \frown \acute{p}(p(\acute{x}(\acute{y}x)))  &amp;amp; (\rm{Frege: Satz 1})      \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; (a;b)(\acute{x}(\acute{y}x))                      &amp;amp; (\rm{Frege: Satz 1})      \\&lt;br /&gt;
                    &amp;amp; = &amp;amp; \acute{x}(\acute{y}x) \frown (a;b)                &amp;amp; (\rm{Frege: Satz 1})      \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; \acute{x}(\acute{y}x) \frown (\acute{\epsilon}(a \frown (b \frown \epsilon))  &amp;amp; (\rm{Def. von}\;a;b) \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; a \frown (b \frown \acute{x}(\acute{y}x))         &amp;amp; (\rm{Frege: Satz 1})      \\&lt;br /&gt;
                    &amp;amp; = &amp;amp; a \frown \acute{y}b                               &amp;amp; (\rm{Frege: Satz 1})      \\&lt;br /&gt;
                    &amp;amp; = &amp;amp; b                                                 &amp;amp; (\rm{Frege: Satz 1})        &lt;br /&gt;
\end{array}&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Frage-Paare als JavaScript-Funktionen==&lt;br /&gt;
&lt;br /&gt;
Da Fregepaare mit Hilfe von Funktionen definiert werden und JavaScript Funktionen als  First-Class-Objekte behandelt,&lt;br /&gt;
kann man Freges Definition sehr leicht mit Hilfe von JavaScript nachbilden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/* Diesen Code können Sie direkt in der JavaScript-Konsole Ihres Browser ausführen. */&lt;br /&gt;
let                              // λx.e  is expressed by  x =&amp;gt; e&lt;br /&gt;
  pair = (o,a) =&amp;gt; ε =&amp;gt; ε(a)(o),  // o;a = λε.εao&lt;br /&gt;
  π1   = p =&amp;gt; p(x =&amp;gt; y =&amp;gt; y),    // π1 = λp.p(λx.λy.y)&lt;br /&gt;
  π2   = p =&amp;gt; p(x =&amp;gt; y =&amp;gt; x),    // π2 = λp.p(λx.λy.x),&lt;br /&gt;
  p    = pair(1111, 2222);&lt;br /&gt;
&lt;br /&gt;
console.log( π1(p) ); // --&amp;gt; 1111&lt;br /&gt;
console.log( π2(p) ); // --&amp;gt; 2222&lt;br /&gt;
&lt;br /&gt;
console.log( π1(ε =&amp;gt; ε(&#039;two&#039;)(&#039;one&#039;)) ); // --&amp;gt; &#039;one&#039;&lt;br /&gt;
console.log( π2(ε =&amp;gt; ε(&#039;two&#039;)(&#039;one&#039;)) ); // --&amp;gt; &#039;two&#039;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Source: [https://glossar.hs-augsburg.de/beispiel/javascript/SetTheory/OrderedPairFregeES6/ EcmaScript6] (&#039;&#039;arrow functions&#039;&#039;),&lt;br /&gt;
[https://glossar.hs-augsburg.de/beispiel/javascript/SetTheory/OrderedPairFregeES5/ EcmaScript5]&lt;br /&gt;
&lt;br /&gt;
Wenn man bereits erzeugte Frege-Paare in einem [[Hasharray]] speichert und diese wiederverwendet,&lt;br /&gt;
kann man sogar erzwingen, dass wertgleiche Paare im Sinne von JavaScript identisch ({{Mono|&amp;amp;#61;&amp;amp;#61;&amp;amp;#61;}}) sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
console.log( pair(&#039;one&#039;,&#039;two&#039;) ===  pair(&#039;one&#039;,&#039;two&#039;));  // --&amp;gt; true &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Source: [https://glossar.hs-augsburg.de/beispiel/javascript/SetTheory/OrderedPairFregeES6Identity/ EcmaScript6] (&#039;&#039;arrow functions&#039;&#039;),&lt;br /&gt;
[https://glossar.hs-augsburg.de/beispiel/javascript/SetTheory/OrderedPairFregeES5Identity/ EcmaScript5]&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Mengenlehre]]&lt;br /&gt;
[[Kategorie:Datenmanagement]]&lt;br /&gt;
[[Kategorie:Datenstruktur]]&lt;br /&gt;
[[Kategorie:Glossar]]&lt;br /&gt;
[[en:Ordered Pair: Definition by Frege]]&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Geordnetes_Paar:_Definition_von_Frege&amp;diff=53269</id>
		<title>Geordnetes Paar: Definition von Frege</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Geordnetes_Paar:_Definition_von_Frege&amp;diff=53269"/>
		<updated>2025-09-30T06:48:04Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Frage-Paare als JavaScript-Funktionen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Qualität&lt;br /&gt;
|correctness         = 4&lt;br /&gt;
|extent              = 4&lt;br /&gt;
|numberOfReferences  = 5&lt;br /&gt;
|qualityOfReferences = 5&lt;br /&gt;
|conformance         = 5&lt;br /&gt;
}}&lt;br /&gt;
Der Artikel „[[Geordnetes Paar]]“ befasst sich mit der Geschichte und den wichtigsten Definitionen des „geordneten Paares“.&lt;br /&gt;
Die älteste mir bekannte Definition, die den Paar-Begriff auf zuvor definierte Grundbegriffe zurückführt,&lt;br /&gt;
ist die Definition von [[Gottlob Frege]].&lt;br /&gt;
&lt;br /&gt;
Da die Symbolik, die Frege verwendet, ungewöhnlich ist &lt;br /&gt;
(dies war einer der Gründe, warum er zu Lebzeiten wenig Anerkennung fand&amp;lt;ref&amp;gt;{{Quelle|Wußing (2009)}}, S. 402&amp;lt;/ref&amp;gt;),&lt;br /&gt;
ist seine Definition nicht ohne Weiters zu verstehen. Im Folgenden soll daher versucht werden, diese Definition etwas zu veranschaulichen.&lt;br /&gt;
 &lt;br /&gt;
==[[Gottlob Frege|Frege]] (1893)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;&amp;gt;{{Quelle|Frege (1893)}}&amp;lt;/ref&amp;gt;==&lt;br /&gt;
[[Datei:Frege (1893), §144, S179.png|mini|300px|Definition des geordneten Paars, Frege (1893), §144, S. 179&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Wir definiren nun das Paar so:&#039;&#039;&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Vdash\acute{\epsilon}(o \frown (a \frown \epsilon)) = o; a&amp;lt;/math&amp;gt; &amp;lt;/div&amp;gt; &lt;br /&gt;
&#039;&#039;Das Semikolon ist hierbei zweiseitiges Functionszeichen.&#039;&#039; (Frege (1893), §144, S. 179&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
==Probleme mit dem Verständnis von Freges Definition==&lt;br /&gt;
Freges Definition ist nicht zu verstehen, wenn man seine Symbolik nicht kennt oder falsch interpretiert.&lt;br /&gt;
Zum Beispiel merkt [[Richard G. Heck Jnr.|Richard Heck]] zu dieser Definition fälschlicherweise an, dass&lt;br /&gt;
sie weder mengentheoretisch noch mit Hilfe der [[Prädikatenlogik zweiter Ordnung]] konsistent nachgebildet werden kann:&lt;br /&gt;
{{Quote|Unfortunately, Frege&#039;s definition of ordered pairs is, as George Boolos once put it, extravagant&lt;br /&gt;
and can not be consistently reconstructed, either in second-order logic or in set-theory.&amp;lt;ref&amp;gt;{{Quelle|Heck (1997)}}&amp;lt;/ref&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
Auch ist nicht notwendigerweise jeder Versuch, diese Definition in eine moderne Form zu zu bringen, von Erfolg gekrönt.&lt;br /&gt;
Der [[C.S. Peirce]] Professor of American Philosophy [[Randall Dipert]] übersetzt Freges Definition beispielsweise folgendermaßen:&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;&amp;lt;x,y&amp;gt; := \{x,\{y\}\}&amp;lt;/math&amp;gt;&amp;lt;ref name=&amp;quot;Dipert&amp;quot;&amp;gt;{{Quelle|Dipert (1982)}}, S. 355&amp;lt;/ref&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
Er merkt an, dass diese Definition falsch ist, weil z.B. die Paare &amp;lt;math&amp;gt;&amp;lt;\{\{c\}\}, c&amp;gt;&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;&amp;lt;\{c\}, \{c\}&amp;gt;&amp;lt;/math&amp;gt; nicht unterschieden werden könnten,&lt;br /&gt;
da sie beide durch &amp;lt;math&amp;gt;\{\{\{c\}\}, \{c\}\}&amp;lt;/math&amp;gt; repräsentiert würden. Das stimmt zwar, ist aber nicht Freges Definition anzulasten,&lt;br /&gt;
sondern Diperts falscher Interpretation dieser Definition.&lt;br /&gt;
&lt;br /&gt;
Gottlob Frege definiert das Paar &amp;lt;math&amp;gt;o;a&amp;lt;/math&amp;gt; mit Hilfe eines „Werthverlaufs“ &amp;lt;math&amp;gt;\acute{\epsilon}\phi(\epsilon)&amp;lt;/math&amp;gt;&lt;br /&gt;
und der „Function &amp;lt;math&amp;gt;\xi \frown \zeta&amp;lt;/math&amp;gt; mit zwei Argumenten“ (Frege (1893, §9 und §34)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;). Da Frege der Funktion &amp;lt;math&amp;gt;\frown&amp;lt;/math&amp;gt;&lt;br /&gt;
keinen Namen gibt, soll diese im Folgenden „Frown“ heißen (in Anlehnung an den Namen des [[LaTeX]]-Symbols, mit dem sie in diesem Beitrag dargestellt wird). &lt;br /&gt;
&lt;br /&gt;
Um die Definition von Frege zu verstehen, muss man die Bedeutung des Wertverlaufs einer Funktion und die Frown-Funktion kennen.&lt;br /&gt;
Auch hier leitet die Literatur einen leicht in die Irre. Beispielsweise interpretiert Edward Zalta (2014, Abschnitt 2.3) die Frown-Operation &lt;br /&gt;
als Element-Operation &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt;.&amp;lt;ref name=&amp;quot;Zalta (2014)&amp;quot;&amp;gt;{{Quelle|Zalta (2014)}}&amp;lt;/ref&amp;gt;&lt;br /&gt;
Das ist richtig, falls der Wertverlauf einer Prädikatsfunktion betrachtet wird, d.h. einer Funktion, die jedes Element des Frege-Universums auf einen [[Wahheitswert]] abbildet. &lt;br /&gt;
Eine derartige Funktion wird von Frege „Begriff“ genannt (Zalta verwendet die englische Übersetzung “concept”). Die Frown-Funktion von Frege ist&lt;br /&gt;
allerdings nicht nur für Wertverläufe von „Begriffen“ definiert, sondern für Wertverläufe von beliebigen Funktionen. Das heißt, i. Allg. kann man &amp;lt;math&amp;gt;\frown&amp;lt;/math&amp;gt; nicht mit &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt; gleichsetzen.&lt;br /&gt;
&lt;br /&gt;
==Wertverlauf und Frown-Funktion==&lt;br /&gt;
Freges Definition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Vdash\acute{\epsilon}(o \frown (a \frown \epsilon)) = o;a&amp;lt;/math&amp;gt; &amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
enthält drei unübliche Symbole, deren Bedeutungen im Folgenden näher beleuchtet werden: &amp;lt;math&amp;gt;\Vdash&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;\acute\cdot&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\frown&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Der Definitionsdoppelstrich===&lt;br /&gt;
&amp;lt;math&amp;gt;\Vdash&amp;lt;/math&amp;gt; ist der „Definitionsdoppelstrich“, der von Frege als Erweiterung des „Urtheilstrichs“ &amp;lt;math&amp;gt;\vdash&amp;lt;/math&amp;gt;&lt;br /&gt;
definiert wird (Frege (1893, §27 und §5)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;). Das Symbol &amp;lt;math&amp;gt;\vdash&amp;lt;/math&amp;gt; wird in der Logik heute noch als  „Herleitungs“-, „Ableitungs“- oder „[[Inferenzoperator]]“ verwendet. &lt;br /&gt;
&lt;br /&gt;
Der Definitionsdoppelstrich sagt zusammen mit dem Gleichheitszeichen &amp;lt;math&amp;gt;=&amp;lt;/math&amp;gt; aus,&lt;br /&gt;
dass der Term &amp;lt;math&amp;gt;o;a&amp;lt;/math&amp;gt; eine abkürzende Schreibweise für den Term &amp;lt;math&amp;gt;\acute{\epsilon}(o \frown (a \frown \epsilon))&amp;lt;/math&amp;gt; ist.&lt;br /&gt;
Heutzutage würde man üblicherweise folgende Schreibweise verwenden:&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;o;a := \acute{\epsilon}(o \frown (a \frown \epsilon)) &amp;lt;/math&amp;gt; &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Der Wertverlaufsoperator===&lt;br /&gt;
Der Operator &amp;lt;math&amp;gt;\acute\cdot&amp;lt;/math&amp;gt; wird von Frege genutzt, um den „Werthverlauf“ einer Funktion zu beschreiben (Frege (1893), §9&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;).&lt;br /&gt;
Laut Kutschera (1989, S. 96)&amp;lt;ref name = &amp;quot;Kutschera&amp;quot;&amp;gt;{{Quelle|Kutschera (1989)}}&amp;lt;/ref&amp;gt; kann man den&lt;br /&gt;
Wertverlauf &amp;lt;math&amp;gt;\acute{\epsilon}\phi(\epsilon)&amp;lt;/math&amp;gt; einer Funktion &amp;lt;math&amp;gt;\phi&amp;lt;/math&amp;gt; in heutigen Klassensystemen &lt;br /&gt;
durch die Klasse &amp;lt;math&amp;gt;\{[\epsilon,\phi(\epsilon)]: \top\}&amp;lt;/math&amp;gt; ausdrücken,&lt;br /&gt;
wobei die Objekte &amp;lt;math&amp;gt;[x,y]&amp;lt;/math&amp;gt; keine Frege-Paare sind, sondern geordnete Paare des zugrundeliegende Klassensystems &lt;br /&gt;
(Wiener-/Kuratowski-/Quine-/Morse-/Schmidt-Paare o. Ä.; siehe Artikel „[[geordnetes Paar]]“). &lt;br /&gt;
&lt;br /&gt;
Für Frege sind Funktionen allerdings grundlegender als Klassen. &lt;br /&gt;
In seinem Axiomensstem sind Funktionen (und deren Wertverläufe) wie beim [[Lambda-Kalkül]] von [[Alonzo Church]] Grundbegriffe, &lt;br /&gt;
die nicht definiert, sondern nur erläutert werden können&amp;lt;ref name = &amp;quot;Kutschera&amp;quot;/&amp;gt;&lt;br /&gt;
und mit deren Hilfe andere Gundbegriffe wie Klassen, Paare, natürliche Zahlen etc. definiert werden. &lt;br /&gt;
&lt;br /&gt;
Denselben Weg schlägt beispielsweise auch John von Neumann (1925) ein.&amp;lt;ref name = &amp;quot;Neumann (1925)&amp;quot;&amp;gt;{{Quelle|Neumann (1925)}}&amp;lt;/ref&amp;gt; &lt;br /&gt;
Allerdings unterscheidet sich sein Axiomensystem&lt;br /&gt;
hinsichtlich der Definition des geordneten Paares in&lt;br /&gt;
zwei wesentlichen Punkten von Freges Axiomensystem:&lt;br /&gt;
&lt;br /&gt;
* Es erlaubt nicht die uneingeschränkte Bildung von Funktionen und geordneten Paaren, so dass die [[Russellsche Antinomie]] mit großer Wahrscheinlichkeit, aber – aufgrund des zweiten [[Gödelscher Unvollständigkeitssatz|Gödelschen Unvollständigkeitssatzes]] – nicht beweisbar vermieden wird.&lt;br /&gt;
* Geordnete Paare werden axiomatisch eingeführt und nicht per Definition.&lt;br /&gt;
&lt;br /&gt;
Überlicherweise werden heute allerdings meist mengen- oder klassenbasierte Systeme (wie z.B. [[Zermelo-Fraenkel-Mengenlehre]], [[Neumann-Bernays-Gödel-Mengenlehre]], [[Quine-Rosser-Mengenlehre]]) zur Formalisierung der Mathematik eingesetzt.&lt;br /&gt;
In diesen Systemen wird zwischen einer Funktion und ihrem Wertverlauf i. Allg. nicht mehr unterschieden. Im Gegenteil: Funktionen werden&lt;br /&gt;
mit Hilfe von Wertverlaufsklassen definiert (siehe z.B. Schmidt (1966)&amp;lt;ref name=&amp;quot;Schmidt (1966)&amp;quot;&amp;gt;{{Quelle|Schmidt (1966)}}&amp;lt;/ref&amp;gt;): &lt;br /&gt;
Eine Funktion &amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; ist eine Klasse von Paaren, wobei es keine zwei Paare &amp;lt;math&amp;gt;[x, y_1], [x, y_2] \in f&amp;lt;/math&amp;gt; gibt, für die&lt;br /&gt;
&amp;lt;math&amp;gt;y_1 \not= y_2&amp;lt;/math&amp;gt; gilt.&lt;br /&gt;
&lt;br /&gt;
Da Funktionen (genauer: Wertverläufe von Funktionen) wesentlich für die Definition des Freges-Paares sind, können Funktionen nicht mit Hilfe des Frege-Paares&lt;br /&gt;
definiert werden. Dies war aber auch nicht die Absicht von Frege. Er benötigte die Paare zur&lt;br /&gt;
Konstruktion von [[reele Zahl|reellen Zahlen]] (vgl. Frege (1903, §161 ff.)&amp;lt;ref&amp;gt;{{Quelle|Frege (1903)}}&amp;lt;/ref&amp;gt;, Kutschera (1989, S.&amp;amp;nbsp;125)&amp;lt;ref name=&amp;quot;Kutschera&amp;quot;/&amp;gt;). &lt;br /&gt;
&lt;br /&gt;
Um sich Frege-Paare zu veranschaulichen, kann man dennoch einen Trick anwenden: Man identifiziert Funktionen mit ihrem Wertverlauf (so wie das in Klassensystemen üblich ist). Das einzige Problem, das man sich dabei einhandelt, ist, dass Frege-Paare &amp;lt;math&amp;gt;a;b&amp;lt;/math&amp;gt; dann – sofern man eine moderne klassenbasierten Theorie zu Hilfe nimmt – mit Hilfe von anderen Paaren &amp;lt;math&amp;gt;[a,b]&amp;lt;/math&amp;gt; definiert werden. Das ist aber nichts Ungewöhnliches. So werden z.B. auch [[Morse-Paar]]e&amp;lt;ref name=&amp;quot;Morse&amp;quot;&amp;gt;{{Quelle|Morse (1965)}}&amp;lt;/ref&amp;gt; mit Hilfe von &lt;br /&gt;
[[Kuratowski-Paar]]en&amp;lt;ref name =&amp;quot;Kuratowski&amp;quot;&amp;gt;{{Quelle|Kuratowski (1921)}}&amp;lt;/ref&amp;gt; definiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man sich den Wertverlauf-Operator als [[Lambda-Kalkül|Lambda-Operator]] vorstellen (vgl. Zalta (2014)&amp;lt;ref name=&amp;quot;Zalta (2014)&amp;quot;/&amp;gt;): &lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\acute{\epsilon}\phi(\epsilon) \;\hat=\; \lambda \epsilon .\phi(\epsilon)&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man die [[Eta-Konversion|η-Konversion]] im Lambda-Kalkül zulässt, sieht man auch hier, dass der Wertverlauf von &amp;lt;math&amp;gt;\phi&amp;lt;/math&amp;gt; mit&lt;br /&gt;
&amp;lt;math&amp;gt;\phi&amp;lt;/math&amp;gt; selbst identifiziert werden kann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\lambda \epsilon .\phi(\epsilon) \;=\; \phi&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Frege betrachtet nicht nur arithmetische Funktionen, sondern auch [[Prädikatsfunktion]]en. &lt;br /&gt;
Diese heißen bei ihm „Begriffe“ (Frege (1893, §3, S. 8)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;).&lt;br /&gt;
Eine Pädikatsfunktion liefert für alle Argumente einen [[Junktor#Nullstellige_Junktoren|Wahrheitswert]] {{Mono|T}} oder {{Mono|F}}&lt;br /&gt;
als Ergebnis. Frege bezeichnet diese Werte als „das Wahre“ und „das Falsche“.&lt;br /&gt;
Wenn &amp;lt;math&amp;gt;\phi&amp;lt;/math&amp;gt; eine Prädikatsfunktion ist, die &#039;&#039;&#039;jedem&#039;&#039;&#039; Objekt im Frege-Universum einen Wahrheitswert zuordnet,&lt;br /&gt;
kann der Wertverlauf von &amp;lt;math&amp;gt;\phi&amp;lt;/math&amp;gt; auch als Klasse aufgefasst werden,&lt;br /&gt;
die alle Elemente (Frege: „Gegenstände“) &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; enthält, für die &amp;lt;math&amp;gt;\phi(\epsilon)&amp;lt;/math&amp;gt; wahr ist.&lt;br /&gt;
Der Wertvelauf &amp;lt;math&amp;gt;\acute{\epsilon}\phi(\epsilon)&amp;lt;/math&amp;gt; entspricht unter dieser Sichtweise der Klasse &amp;lt;math&amp;gt;\{\epsilon: \phi(\epsilon)\}&amp;lt;/math&amp;gt; (vgl. Kutschera (1989, Kapitel 6.4)&amp;lt;ref name = &amp;quot;Kutschera&amp;quot;/&amp;gt;). &lt;br /&gt;
Insbesondere definiert Frege die leere Menge und einelementige Mengen mit Hilfe von Wertverläufen (führt dafür aber keine eigenen Symbole ein):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
!             || moderne Definition            || Freges Definition || Quellen&lt;br /&gt;
|- &lt;br /&gt;
| leere Menge || &amp;lt;math&amp;gt;\emptyset := \{x: x\not=x\}&amp;lt;/math&amp;gt; || &amp;lt;math&amp;gt;\acute{\epsilon}\neg(\epsilon=\epsilon)&amp;lt;/math&amp;gt; || [[Klasse_(Mengenlehre)#Spezielle_Klassen_Anlehnung_Schmidt.5B2.5D|Klasse]], Frege (1893, S. 53, vorletzter Absatz)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Einermenge || &amp;lt;math&amp;gt;\{a\} := \{x: x=a\}&amp;lt;/math&amp;gt; oder&amp;lt;br/&amp;gt;&amp;lt;math&amp;gt;\{a\} := \{x: \rm{Mg}(a) \rightarrow x = a\}&amp;lt;/math&amp;gt; || &amp;lt;math&amp;gt;\acute{\epsilon}(\epsilon=a)&amp;lt;/math&amp;gt; || [[Klasse_(Mengenlehre)#Spezielle_Klassen_Anlehnung_Schmidt.5B2.5D|Klasse]], Frege (1893, §11)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
| Extraktion des Elements&amp;lt;br/&amp;gt;einer Einermenge || Es sei &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; eine Menge.&amp;lt;br/&amp;gt;&amp;lt;math&amp;gt;A =\{a\} \rightarrow \bigcup{A} = a&amp;lt;/math&amp;gt; ||Wenn &amp;lt;math&amp;gt;A =\acute{\epsilon}(\epsilon=a)&amp;lt;/math&amp;gt;, dann &amp;lt;math&amp;gt;\backslash A = a&amp;lt;/math&amp;gt; || &lt;br /&gt;
Schmidt (1966, S72, 8.28)&amp;lt;ref name=&amp;quot;Schmidt (1966)&amp;quot;/&amp;gt;, Frege (1893, §11)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Für alle Objekte &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt;, die keine Einermenge sind, setzt Frege fest, dass &amp;lt;math&amp;gt;\backslash A = A&amp;lt;/math&amp;gt; gilt.&lt;br /&gt;
Das heißt, für die leere Menge liefern &amp;lt;math&amp;gt;\bigcup&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\backslash&amp;lt;/math&amp;gt; ebenfalls dasselbe Ergebnis (nämlich die leere Menge).&lt;br /&gt;
Für alle anderen Objekte &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; unterscheiden sich die Ergebnisse von &amp;lt;math&amp;gt;\bigcup A&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\backslash A&amp;lt;/math&amp;gt; dagegen. Dies ist für&lt;br /&gt;
die Veranschaulichung der Frown-Funktion, die im nachfolgenden Abschnitt beschrieben wird, jedoch nicht von Belang,&lt;br /&gt;
da bei der Definition dieser Funktion nur die leere Menge und einelementige Mengen benötigt werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Frege_1893_§34_S53.png|mini|300px|Definition der Frown-Funktion, Frege (1893, §34, S.&amp;amp;nbsp;53)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
===Die Frown-Funktion===&lt;br /&gt;
&lt;br /&gt;
Frege definiert die Frown-Funktion folgendermaßen (Frege (1893, §34, S. 53)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Vdash\backslash\acute{\alpha}(\neg\bigwedge g : u = \acute{\epsilon}g(\epsilon) \rightarrow \neg (g(a) = \alpha)) = a \frown u&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
Dies kann man mit Hilfe der Operatoren &amp;lt;math&amp;gt;\bigvee&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\wedge&amp;lt;/math&amp;gt; (die Frege allerdings nicht explizit definiert und daher auch nicht explitizt verwendet)&lt;br /&gt;
etwas einfacher ausdrücken, da &amp;lt;math&amp;gt;\neg \bigwedge g: a \rightarrow \neg b&amp;lt;/math&amp;gt; äquivalent zu &amp;lt;math&amp;gt;\bigvee g: a \wedge b &amp;lt;/math&amp;gt; ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Vdash\backslash\acute{\alpha}(\bigvee g : u = \acute{\epsilon}g(\epsilon) \wedge g(a) = \alpha) = a \frown u&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einem modernen Klassensystem kann man diese Definition – da es sich bei &amp;lt;math&amp;gt;\bigvee g : \ldots&amp;lt;/math&amp;gt; um eine Prädikatsfunktion handelt und es höchstens ein Element &amp;lt;math&amp;gt;\alpha&amp;lt;/math&amp;gt; gibt, das dieses Prädikat erfüllt, da also &amp;lt;math&amp;gt;\backslash A&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\bigcup A&amp;lt;/math&amp;gt; dasselbe Ergebnis liefern – folgendermaßen formulieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;a \frown u := \bigcup\{\alpha : \bigvee g : u = \acute{\epsilon}g(\epsilon) \wedge g(a) = \alpha\}&amp;lt;/math&amp;gt;&lt;br /&gt;
(der Wertlauf &amp;lt;math&amp;gt;\acute{\epsilon}g(\epsilon)&amp;lt;/math&amp;gt; repräsentiert hier natürlich die Klasse &amp;lt;math&amp;gt;\{[\epsilon,y]: y=g(\epsilon)\}&amp;lt;/math&amp;gt;)&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zwei Fälle sind zu unterscheiden: Entweder ist &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; der Wertverlauf einer Funktion g oder &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; ist dies nicht.&lt;br /&gt;
Im ersten Fall gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\vdash a \frown \acute{\epsilon}g(\epsilon) = \bigcup\{\alpha : g(a) = \alpha\} = g(a)&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Frege beweist auf den Seiten 74 und 75 denselben Satz in folgender Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\vdash f(a) = a \frown \acute{\epsilon}f(\epsilon)&amp;lt;/math&amp;gt; (Dieser Satz erhält von Frege die Satznummer 1.)&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im zweiten Fall gilt:&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\vdash a \frown u = \bigcup\{\alpha : \bigvee g : u = \acute{\epsilon}g(\epsilon) \wedge g(a) = \alpha\} = \bigcup\{\alpha : \bot\} = \emptyset&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;a \frown u&amp;lt;/math&amp;gt; liefert also, falls u der Wertverlauf einer Funktion &amp;lt;math&amp;gt;g&amp;lt;/math&amp;gt; ist, den Wert der Funktion &amp;lt;math&amp;gt;g&amp;lt;/math&amp;gt; für das Argument &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; als Ergebnis.&lt;br /&gt;
Falls &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; kein Wertverlauf ist, hat &amp;lt;math&amp;gt;a \frown u&amp;lt;/math&amp;gt; dagegen die leere Menge als Ergebnis. (vgl. Frege (1893, S. 53, letzter Absatz)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;; Kutschera (1989, S. 104)&amp;lt;ref name = &amp;quot;Kutschera&amp;quot;/&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Im Falle einer Prädikatsfunktion entspricht der &amp;lt;math&amp;gt;\frown&amp;lt;/math&amp;gt;-Operator dem in Klassensystem üblichen Elementoperator &amp;lt;math&amp;gt;\in&amp;lt;/math&amp;gt;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;a \frown \acute{\epsilon}g(\epsilon) = g(a)&amp;lt;/math&amp;gt; entspricht in diesem Fall folgender Beziehung &lt;br /&gt;
                     &amp;lt;math&amp;gt;a \in \{\epsilon: g(\epsilon)\} \leftrightarrow g(a)&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist der Spezialfall, mit dem sich Zalta näher befasst&amp;lt;ref name=&amp;quot;Zalta (2014)&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Frege 1893 S75.png|mini|300px|Satz 1 und Satz 2: Der Wertverlauf von ein- und zweistelligen Funktionen, Frege (1893), S. 75&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;]]&lt;br /&gt;
Frege zeigt auf S. 75 außerdem noch, dass sein Satz 1 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\vdash f(a) = a \frown \acute{\epsilon}f(\epsilon)&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auch für zweistellige Funktionen verallgemeinert werden kann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\vdash f(a,b) = a \frown (b \frown \acute{\alpha}\acute{\epsilon}f(\epsilon,\alpha))&amp;lt;/math&amp;gt; (Dieser Satz erhält von Frege die Satznummer 2.)&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analog zu Frege kann man auch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\vdash f(a)(b) = a \frown (b \frown \acute{\alpha}\acute{\epsilon}f(\epsilon)(\alpha))&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
zeigen. Das heißt, &amp;lt;math&amp;gt;f(a,b)&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;f(a)(b)&amp;lt;/math&amp;gt; unterscheiden sich im Prinzip nicht. Obwohl Frege diese alternative &lt;br /&gt;
Notation für Funktionen mit zwei Argumenten nicht verwendet hat, wird dsie im nächsten Abschnitt zur&lt;br /&gt;
Veranschaulichung der Paardefinition verwendet, um die Definitionen etwas schlanker zu halten.&lt;br /&gt;
&lt;br /&gt;
Anmerkung: Auf dieselbe Weise könnte man auch den Wertverlauf von beliebigen n-stelligen Funktionen definieren. Dies hat Frege allerdings nicht getan.&lt;br /&gt;
&lt;br /&gt;
===„Übersetzung“ von Freges Definition in „moderne“ Symbolik===&lt;br /&gt;
&lt;br /&gt;
Aus den vorangegangenen Überlegungen folgt, dass man in modernen Klassensystemen Freges Paardefinition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\Vdash\acute{\epsilon}(o \frown (a \frown \epsilon)) = o; a&amp;lt;/math&amp;gt; &amp;lt;/div&amp;gt; &lt;br /&gt;
&lt;br /&gt;
mit Hilfe des [[Lambda-Kalkül]]s (das dem Axiomensystem von Frege gerade hinsichtlich des Wertverlauf-Operators etwas näher steht als ein Klassensystem) folgendermaßen formulieren kann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;o;a \,:= \; \lambda \epsilon . \epsilon a o&amp;lt;/math&amp;gt; &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für die „Übersetzung“ von Freges Definition in eine klassenbasierte Definition werden folgende Vereinbarungen getroffen.&lt;br /&gt;
&lt;br /&gt;
* Eine Funktion &amp;lt;math&amp;gt;f&amp;lt;/math&amp;gt; ist eine Klasse von Paaren mit &amp;lt;math&amp;gt;[x,y_1], [x,y_2]\in f \rightarrow y_1=y_2&amp;lt;/math&amp;gt;.&lt;br /&gt;
* Der Funktionsaufruf-Operator &amp;lt;math&amp;gt;\cdot(\cdot)&amp;lt;/math&amp;gt; ist folgendermaßen definiert &amp;lt;math&amp;gt;f(x) := \bigcap\{y: [x,y] \in f \}&amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Damit definiert man:&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;o; a := \{ [\epsilon, \epsilon(a)(o)] : \top\}&amp;lt;/math&amp;gt;, d.h, &amp;lt;math&amp;gt;o; a&amp;lt;/math&amp;gt; ist eine Funktion mit &amp;lt;math&amp;gt;(o; a)(\epsilon) = \epsilon(a)(o)&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sofern &amp;lt;math&amp;gt;\epsilon&amp;lt;/math&amp;gt; eine Funktion ist, für die der zweistellige Funktionsaufruf &amp;lt;math&amp;gt;\epsilon(a)(o)&amp;lt;/math&amp;gt; definiert ist, stimmen Freges Definition und die &lt;br /&gt;
Alternative-Definition überein. Für andere Funktionen und Nicht-Funktionen gilt dagegen bei Frege stets &amp;lt;math&amp;gt;(o;a)(\epsilon) = \emptyset&amp;lt;/math&amp;gt;.&lt;br /&gt;
Dies ist bei der Alternativdefinition nicht der Fall.&lt;br /&gt;
Allerdings ist es für die weiteren Betrachtungen nicht wichtig, welchen Wert &amp;lt;math&amp;gt;(o;a)(\epsilon)&amp;lt;/math&amp;gt; annimmt, falls &amp;lt;math&amp;gt;\epsilon(a)(o)&amp;lt;/math&amp;gt; nicht definiert ist.&lt;br /&gt;
&lt;br /&gt;
==Das Paaraxiom==&lt;br /&gt;
&lt;br /&gt;
[[Datei:Frege 1893 S195.png|mini|300px|Beweis des Paaraxioms, Teil 1, Frege (1893, S.&amp;amp;nbsp;195)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;]]&lt;br /&gt;
[[Datei:Frege 1893 S185.png|mini|300px|Beweis des Paaraxioms, Teil 2, Frege (1893, S.&amp;amp;nbsp;185)&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;]]&lt;br /&gt;
Frege beweist die Gültigkeit des [[Paaraxiom]]s direkt, {{dh}} ohne Hilfe von Projektionsoperatoren und des Satzes &lt;br /&gt;
[[Satz:Aus der Existenz zweier Projektionsoperatoren für geordnete Paare folgt das Paaraxiom|„Aus der Existenz zweier Projektionsoperatoren für geordnete Paare folgt das Paaraxiom“]]:&lt;br /&gt;
&lt;br /&gt;
{{TBD}}&lt;br /&gt;
&lt;br /&gt;
===Die Projektionsoperatoren===&lt;br /&gt;
&lt;br /&gt;
Frege gibt für seine Paardefinition keine [[Projektionsoperatoren]] an. Es ist aber kein Problem, diese zu definieren.&lt;br /&gt;
Und damit ist ebenfalls sichergestellt, &lt;br /&gt;
[[Satz:Aus der Existenz zweier Projektionsoperatoren für geordnete Paare folgt das Paaraxiom|dass das Paaraxiom erfüllt ist]].&lt;br /&gt;
&lt;br /&gt;
Die Definition der Projektionsoperatoren kann in modernen Axiomensystemen auf (mindestens) zwei Arten erfolgen:&lt;br /&gt;
&lt;br /&gt;
# Mit Funktionen eines Mengen- oder Klassensystems.&lt;br /&gt;
# Mit Hilfe des Lambda-Kalküls.&lt;br /&gt;
&lt;br /&gt;
Man stellt fest, dass im zweiten Fall die Projektionsoperatoren wesentlich prägnanter formuliert werden können.&lt;br /&gt;
Das liegt daran, dass es sich sowohl beim Lambda-Kalkül, als auch beim Axiomensystem von Frege um funktionale Axiomensysteme handelt.&lt;br /&gt;
&lt;br /&gt;
====Die Projektionsoperatoren auf Basis von Funktionen eines Mengen- oder Klassensystems====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\begin{array}{lclclclclclcl}&lt;br /&gt;
  \rm{id}       &amp;amp; := &amp;amp; \{[x,x] : \top \}            &amp;amp; \rm{d.h.:} &amp;amp; \rm{id}(x)      &amp;amp; = &amp;amp; x                       \\&lt;br /&gt;
  \rm{const_c}  &amp;amp; := &amp;amp; \{[x,c] : \top \}            &amp;amp; \rm{d.h.:} &amp;amp; \rm{const_c}(x) &amp;amp; = &amp;amp; c                       \\&lt;br /&gt;
  \pi&#039;_1        &amp;amp; := &amp;amp; \{[x,\rm{id}] : \top \}      &amp;amp; \rm{d.h.:} &amp;amp; \pi&#039;_1(x)       &amp;amp; = &amp;amp; \rm{id}                 \\      &lt;br /&gt;
                &amp;amp;    &amp;amp;                              &amp;amp; \rm{d.h.:} &amp;amp; \pi&#039;_1(x)(y)    &amp;amp; = &amp;amp; \rm{id}(y) =  y         \\&lt;br /&gt;
  \pi&#039;_2        &amp;amp; := &amp;amp; \{[x,\rm{const}_x] : \top \} &amp;amp; \rm{d.h.:} &amp;amp; \pi&#039;_2(x)       &amp;amp; = &amp;amp; \rm{const}_x            \\&lt;br /&gt;
                &amp;amp;    &amp;amp;                              &amp;amp; \rm{d.h.:} &amp;amp; \pi&#039;_2(x)(y)    &amp;amp; = &amp;amp; \rm{const}_x(y) = x     \\&lt;br /&gt;
  \pi_1         &amp;amp; := &amp;amp; \{ [p, p(\pi&#039;_1)] : \top\}   &amp;amp; \rm{d.h.:} &amp;amp; \pi_1(p)        &amp;amp; = &amp;amp; p(\pi&#039;_1)               \\&lt;br /&gt;
  \pi_2         &amp;amp; := &amp;amp; \{ [p, p(\pi&#039;_2)] : \top\}   &amp;amp; \rm{d.h.:} &amp;amp; \pi_2(p)        &amp;amp; = &amp;amp; p(\pi&#039;_2)&lt;br /&gt;
\end{array}&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\pi_1&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\pi_2&amp;lt;/math&amp;gt; sind die gesuchten Projektionsoperatoren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\begin{array}{lcll} &lt;br /&gt;
  \pi_1(a;b) &amp;amp; = &amp;amp; (a;b) (\pi&#039;_1) &amp;amp; (\rm{Def. von}\;\pi_1) \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; \pi&#039;_1(b)(a)   &amp;amp; (\rm{Def. von}\;a;b)   \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; \rm{id}(a)     &amp;amp; (\rm{Def. von}\;\pi&#039;_1)\\ &lt;br /&gt;
             &amp;amp; = &amp;amp; a              &amp;amp; (\rm{Def. von}\;\rm{id})&lt;br /&gt;
\\ &lt;br /&gt;
  \pi_2(a;b) &amp;amp; = &amp;amp; (a;b) (\pi&#039;_2) &amp;amp; (\rm{Def. von}\;\pi_2) \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; \pi&#039;_2(b)(a)   &amp;amp; (\rm{Def. von}\;a;b)   \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; \rm{const}_b(a)&amp;amp; (\rm{Def. von}\;\pi&#039;_2)\\ &lt;br /&gt;
             &amp;amp; = &amp;amp; b              &amp;amp; (\rm{Def. von}\;\rm{const}_b)&lt;br /&gt;
\end{array}&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Die Projektionsoperatoren auf Basis des Lambda-Kalküls====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\begin{array}{lclclcl} &lt;br /&gt;
  \pi_1 &amp;amp; := &amp;amp; \lambda p. p(\lambda x.\lambda y.y) \\ &lt;br /&gt;
  \pi_2 &amp;amp; := &amp;amp; \lambda p. p(\lambda x.\lambda y.x)&lt;br /&gt;
\end{array}&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\pi_1&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\pi_2&amp;lt;/math&amp;gt; sind die gesuchten Projektionsoperatoren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\begin{array}{lcll} &lt;br /&gt;
  \pi_1(a;b) &amp;amp; = &amp;amp; (a;b) (\lambda x.\lambda y.y)                                    &amp;amp; (\rm{Def. von}\;\pi_1)    \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; (\lambda \epsilon . \epsilon b  a)(\lambda x. \lambda y. y)      &amp;amp; (\rm{Def. von}\;a;b)      \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; (\lambda x. \lambda y. y) b a                                    &amp;amp; (\rm{\beta{-}Konversion}) \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; (\lambda y. y) a                                                 &amp;amp; (\rm{\beta{-}Konversion}) \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; a                                                                &amp;amp; (\rm{\beta{-}Konversion})&lt;br /&gt;
\\ &lt;br /&gt;
  \pi_2(a;b) &amp;amp; = &amp;amp; (a;b) (\lambda x.\lambda y.x)                                    &amp;amp; (\rm{Def. von}\;\pi_2)    \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; (\lambda \epsilon . \epsilon  b  a)(\lambda x. \lambda y. x)     &amp;amp; (\rm{Def. von}\;a;b)      \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; (\lambda x. \lambda y. x) b a                                    &amp;amp; (\rm{\beta{-}Konversion}) \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; (\lambda y. b) a                                                 &amp;amp; (\rm{\beta{-}Konversion}) \\ &lt;br /&gt;
             &amp;amp; = &amp;amp; b                                                                &amp;amp; (\rm{\beta{-}Konversion}) \\ &lt;br /&gt;
\end{array} &amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====„Rückübersetzung“ der Projektionsoperatoren in Freges Notation====&lt;br /&gt;
&lt;br /&gt;
Die Projektionsoperatoren des vorangegangenen Abschnitts können ziemlich einfach in&lt;br /&gt;
Freges Notation übersetzt werden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\begin{array}{lclclcl} &lt;br /&gt;
  \Vdash \acute{p}(p(\acute{x}(\acute{y}y))) = \pi_1 \\&lt;br /&gt;
  \Vdash \acute{p}(p(\acute{x}(\acute{y}x))) = \pi_2 \\&lt;br /&gt;
\end{array}&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\pi_1&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\pi_2&amp;lt;/math&amp;gt; sind die gesuchten Projektionsoperatoren. Der Beweis des Satzes basiert im Wesentlichen&lt;br /&gt;
auf Satz 1 von Frege: &amp;lt;math&amp;gt;\vdash f(a) = a \frown \acute{\epsilon}f(\epsilon)&amp;lt;/math&amp;gt; (§54, S. 75&amp;lt;ref name=&amp;quot;Frege (1893)&amp;quot;/&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;formula&amp;quot;&amp;gt;&amp;lt;math&amp;gt;\begin{array}{lcll} &lt;br /&gt;
  \vdash \pi_1(a;b) &amp;amp; = &amp;amp; \acute{p}(p(\acute{x}(\acute{y}y)))(a;b)          &amp;amp; (\rm{Def. von}\;\pi_1)    \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; (a;b) \frown \acute{p}(p(\acute{x}(\acute{y}y)))  &amp;amp; (\rm{Frege: Satz 1})      \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; (a;b)(\acute{x}(\acute{y}y))                      &amp;amp; (\rm{Frege: Satz 1})      \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; \acute{x}(\acute{y}y) \frown (a;b)                &amp;amp; (\rm{Frege: Satz 1})      \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; \acute{x}(\acute{y}y) \frown (\acute{\epsilon}(a \frown (b \frown \epsilon)) &amp;amp; (\rm{Def. von}\;a;b) \\&lt;br /&gt;
                    &amp;amp; = &amp;amp; a \frown (b \frown \acute{x}(\acute{y}y))         &amp;amp; (\rm{Frege: Satz 1})      \\&lt;br /&gt;
                    &amp;amp; = &amp;amp; a \frown \acute{y}y                               &amp;amp; (\rm{Frege: Satz 1})      \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; a                                                 &amp;amp; (\rm{Frege: Satz 1})     &lt;br /&gt;
\\ &lt;br /&gt;
  \vdash \pi_2(a;b) &amp;amp; = &amp;amp; \acute{p}(p(\acute{x}(\acute{y}x)))(a;b)          &amp;amp; (\rm{Def. von}\;\pi_2)    \\&lt;br /&gt;
                    &amp;amp; = &amp;amp; (a;b) \frown \acute{p}(p(\acute{x}(\acute{y}x)))  &amp;amp; (\rm{Frege: Satz 1})      \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; (a;b)(\acute{x}(\acute{y}x))                      &amp;amp; (\rm{Frege: Satz 1})      \\&lt;br /&gt;
                    &amp;amp; = &amp;amp; \acute{x}(\acute{y}x) \frown (a;b)                &amp;amp; (\rm{Frege: Satz 1})      \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; \acute{x}(\acute{y}x) \frown (\acute{\epsilon}(a \frown (b \frown \epsilon))  &amp;amp; (\rm{Def. von}\;a;b) \\ &lt;br /&gt;
                    &amp;amp; = &amp;amp; a \frown (b \frown \acute{x}(\acute{y}x))         &amp;amp; (\rm{Frege: Satz 1})      \\&lt;br /&gt;
                    &amp;amp; = &amp;amp; a \frown \acute{y}b                               &amp;amp; (\rm{Frege: Satz 1})      \\&lt;br /&gt;
                    &amp;amp; = &amp;amp; b                                                 &amp;amp; (\rm{Frege: Satz 1})        &lt;br /&gt;
\end{array}&amp;lt;/math&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Frage-Paare als JavaScript-Funktionen==&lt;br /&gt;
&lt;br /&gt;
Da Fregepaare mit Hilfe von Funktionen definiert werden und JavaScript Funktionen als  First-Class-Objekte behandelt,&lt;br /&gt;
kann man Freges Definition sehr leicht mit Hilfe von JavaScript nachbilden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/* Diesen Code können Sie direkt in der JavaScript-Konsole Ihres Browser ausführen. */&lt;br /&gt;
let                              // λx.e  is expressed by  x =&amp;gt; e&lt;br /&gt;
  pair = (o,a) =&amp;gt; ε =&amp;gt; ε(a)(o),  // o;a = λε.εao&lt;br /&gt;
  π1   = p =&amp;gt; p(x =&amp;gt; y =&amp;gt; y),    // π1 = λp.p(λx.λy.y)&lt;br /&gt;
  π2   = p =&amp;gt; p(x =&amp;gt; y =&amp;gt; x),    // π2 = λp.p(λx.λy.x),&lt;br /&gt;
  p    = pair(1111, 2222);&lt;br /&gt;
&lt;br /&gt;
console.log( π1(p) ); // --&amp;gt; 1111&lt;br /&gt;
console.log( π2(p) ); // --&amp;gt; 2222&lt;br /&gt;
&lt;br /&gt;
console.log( π1(ε =&amp;gt; ε(&#039;two&#039;)(&#039;one&#039;)) );  // --&amp;gt; &#039;one&#039;&lt;br /&gt;
console.log( π2(ε =&amp;gt; ε(&#039;two&#039;)(&#039;one&#039;&#039;)) ); // --&amp;gt; &#039;two&#039;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Source: [https://glossar.hs-augsburg.de/beispiel/javascript/SetTheory/OrderedPairFregeES6/ EcmaScript6] (&#039;&#039;arrow functions&#039;&#039;),&lt;br /&gt;
[https://glossar.hs-augsburg.de/beispiel/javascript/SetTheory/OrderedPairFregeES5/ EcmaScript5]&lt;br /&gt;
&lt;br /&gt;
Wenn man bereits erzeugte Frege-Paare in einem [[Hasharray]] speichert und diese wiederverwendet,&lt;br /&gt;
kann man sogar erzwingen, dass wertgleiche Paare im Sinne von JavaScript identisch ({{Mono|&amp;amp;#61;&amp;amp;#61;&amp;amp;#61;}}) sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
console.log( pair(&#039;one&#039;,&#039;two&#039;) ===  pair(&#039;one&#039;,&#039;two&#039;));  // --&amp;gt; true &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Source: [https://glossar.hs-augsburg.de/beispiel/javascript/SetTheory/OrderedPairFregeES6Identity/ EcmaScript6] (&#039;&#039;arrow functions&#039;&#039;),&lt;br /&gt;
[https://glossar.hs-augsburg.de/beispiel/javascript/SetTheory/OrderedPairFregeES5Identity/ EcmaScript5]&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Mengenlehre]]&lt;br /&gt;
[[Kategorie:Datenmanagement]]&lt;br /&gt;
[[Kategorie:Datenstruktur]]&lt;br /&gt;
[[Kategorie:Glossar]]&lt;br /&gt;
[[en:Ordered Pair: Definition by Frege]]&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=GlossarWiki:B%C3%BCcher/Multimedia-Datenbanken/WiSe2025&amp;diff=53268</id>
		<title>GlossarWiki:Bücher/Multimedia-Datenbanken/WiSe2025</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=GlossarWiki:B%C3%BCcher/Multimedia-Datenbanken/WiSe2025&amp;diff=53268"/>
		<updated>2025-09-19T15:06:32Z</updated>

		<summary type="html">&lt;p&gt;Kowa: Weiterleitungsziel von GlossarWiki:Bücher/Datenmanagementn/WiSe2025 nach GlossarWiki:Bücher/Datenmanagement/WiSe2025 geändert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#WEITERLEITUNG [[GlossarWiki:Bücher/Datenmanagement/WiSe2025]]&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=GlossarWiki:B%C3%BCcher/Datenmanagement/WiSe2025&amp;diff=53265</id>
		<title>GlossarWiki:Bücher/Datenmanagement/WiSe2025</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=GlossarWiki:B%C3%BCcher/Datenmanagement/WiSe2025&amp;diff=53265"/>
		<updated>2025-09-19T15:04:32Z</updated>

		<summary type="html">&lt;p&gt;Kowa: Kowa verschob die Seite GlossarWiki:Bücher/Datenmanagementn/WiSe2025 nach GlossarWiki:Bücher/Datenmanagement/WiSe2025&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Multimedia-Datenbanken – Skript zur Vorlesung im WiSe 2025/2026 ===&lt;br /&gt;
Autor: [[Wolfgang Kowarschick]]&lt;br /&gt;
&lt;br /&gt;
;Praktikum&lt;br /&gt;
: [[Händler-Datenbank (SQL-Beispiel)|Die Händler-Datenbank]]&lt;br /&gt;
: Die [[Händler-Datenbank (SQL-Beispiel)/Identität|Identität]] am Beispiel der Händler-Datenbank&lt;br /&gt;
: Die [[Händler-Datenbank (SQL-Beispiel)/Projektion|Projektion]] am Beispiel der Händler-Datenbank&lt;br /&gt;
: Die [[Händler-Datenbank (SQL-Beispiel)/Selektion|Selektion]] am Beispiel der Händler-Datenbank&lt;br /&gt;
&lt;br /&gt;
;Vorlesung&lt;br /&gt;
: [[UML-Diagramme: Typische Fehler]]&lt;br /&gt;
: [[Relationale Algebra]]&lt;br /&gt;
: [[GlossarWiki:Bücher/Datenbanksysteme|Theorie]]&lt;br /&gt;
: [[:Kategorie:Datenmanagement|einschlägige Artikel im GlossarWiki]] &lt;br /&gt;
&lt;br /&gt;
;Literatur&lt;br /&gt;
: [https://glossar.hs-augsburg.de/w/media/2/2f/MMDB2018sose.pdf MMDB-Skript] (Quelle: [[Kowarschick (MMDB-Skript)]])&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Buch|Multimedia-Programmierung]]&lt;br /&gt;
[[Kategorie:Multimedia-Programmierung]]&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=GlossarWiki:B%C3%BCcher/Multimedia-Datenbanken/WiSe2025&amp;diff=53264</id>
		<title>GlossarWiki:Bücher/Multimedia-Datenbanken/WiSe2025</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=GlossarWiki:B%C3%BCcher/Multimedia-Datenbanken/WiSe2025&amp;diff=53264"/>
		<updated>2025-09-19T15:03:38Z</updated>

		<summary type="html">&lt;p&gt;Kowa: Kowa verschob die Seite GlossarWiki:Bücher/Multimedia-Datenbanken/WiSe2025 nach GlossarWiki:Bücher/Datenmanagementn/WiSe2025&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#WEITERLEITUNG [[GlossarWiki:Bücher/Datenmanagementn/WiSe2025]]&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=GlossarWiki:B%C3%BCcher/Datenmanagement/WiSe2025&amp;diff=53263</id>
		<title>GlossarWiki:Bücher/Datenmanagement/WiSe2025</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=GlossarWiki:B%C3%BCcher/Datenmanagement/WiSe2025&amp;diff=53263"/>
		<updated>2025-09-19T15:03:38Z</updated>

		<summary type="html">&lt;p&gt;Kowa: Kowa verschob die Seite GlossarWiki:Bücher/Multimedia-Datenbanken/WiSe2025 nach GlossarWiki:Bücher/Datenmanagementn/WiSe2025&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Multimedia-Datenbanken – Skript zur Vorlesung im WiSe 2025/2026 ===&lt;br /&gt;
Autor: [[Wolfgang Kowarschick]]&lt;br /&gt;
&lt;br /&gt;
;Praktikum&lt;br /&gt;
: [[Händler-Datenbank (SQL-Beispiel)|Die Händler-Datenbank]]&lt;br /&gt;
: Die [[Händler-Datenbank (SQL-Beispiel)/Identität|Identität]] am Beispiel der Händler-Datenbank&lt;br /&gt;
: Die [[Händler-Datenbank (SQL-Beispiel)/Projektion|Projektion]] am Beispiel der Händler-Datenbank&lt;br /&gt;
: Die [[Händler-Datenbank (SQL-Beispiel)/Selektion|Selektion]] am Beispiel der Händler-Datenbank&lt;br /&gt;
&lt;br /&gt;
;Vorlesung&lt;br /&gt;
: [[UML-Diagramme: Typische Fehler]]&lt;br /&gt;
: [[Relationale Algebra]]&lt;br /&gt;
: [[GlossarWiki:Bücher/Datenbanksysteme|Theorie]]&lt;br /&gt;
: [[:Kategorie:Datenmanagement|einschlägige Artikel im GlossarWiki]] &lt;br /&gt;
&lt;br /&gt;
;Literatur&lt;br /&gt;
: [https://glossar.hs-augsburg.de/w/media/2/2f/MMDB2018sose.pdf MMDB-Skript] (Quelle: [[Kowarschick (MMDB-Skript)]])&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Buch|Multimedia-Programmierung]]&lt;br /&gt;
[[Kategorie:Multimedia-Programmierung]]&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Hauptseite/Kategorien&amp;diff=53262</id>
		<title>Hauptseite/Kategorien</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Hauptseite/Kategorien&amp;diff=53262"/>
		<updated>2025-09-19T15:03:20Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Kategorien */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Kategorien==&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%&amp;quot;&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#dddddd; width:50%&amp;quot; align=&amp;quot;center&amp;quot;| &#039;&#039;&#039;Studienpläne&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#dddddd; width:50%&amp;quot; align=&amp;quot;center&amp;quot;| &#039;&#039;&#039;Lehrveranstaltungen&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#fffff1;&amp;quot;  align=&amp;quot;left&amp;quot;| &lt;br /&gt;
* [[Bachelor-Studiengang Interaktive Medien (SPO 2018), {{BA}}]]&lt;br /&gt;
* [[Bachelor-Studiengang Interaktive Medien (SPO 2018), {{BSc}}]]&lt;br /&gt;
* [[Master-Studiengang Interaktive Mediensysteme (SPO 2014)|Master-Studiengang Interaktive Mediensysteme]]&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#fffff1;&amp;quot;  align=&amp;quot;left&amp;quot;| &lt;br /&gt;
* [[GlossarWiki:Bücher/Datenmanagement/WiSe2025|Datenmanagement]]  (WiSe 2025/2026)&lt;br /&gt;
* [[GlossarWiki:Bücher/Full-Stack-Webentwicklung/SoSe2025|Full-Stack-Webentwicklung]]  (SoSe 2025)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;width: 100%&amp;quot;&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#dddddd; width:33.3%&amp;quot; align=&amp;quot;center&amp;quot;| &#039;&#039;&#039;Schwerpunktthemen&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#dddddd; width:33.3%&amp;quot; align=&amp;quot;center&amp;quot;| &#039;&#039;&#039;{{HSA}}-Kategorien&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#dddddd; width:33.3%&amp;quot; align=&amp;quot;center&amp;quot;| &#039;&#039;&#039;Klassifizierende Kategorien&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#fffff1;&amp;quot;  align=&amp;quot;left&amp;quot;| &lt;br /&gt;
*[[:Kategorie:Logik|Logik]]&lt;br /&gt;
*[[:Kategorie:Mengenlehre|Mengenlehre]]&lt;br /&gt;
*[[:Kategorie:Stochastik|Stochastik]]&lt;br /&gt;
**[[:Kategorie:Zufallsexperiment|Zufallsexperiment]]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#fffff1;&amp;quot;  align=&amp;quot;left&amp;quot;| &lt;br /&gt;
&#039;&#039;&#039;Allgemein&#039;&#039;&#039;&lt;br /&gt;
*[[:Kategorie: HSA|Hochschule Augsburg]] &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&#039;&#039;&#039;Fachgebiete&#039;&#039;&#039;&lt;br /&gt;
*[[:Kategorie: Allgemeinwissenschaften|Allgemeinwissenschaften]]&lt;br /&gt;
&amp;lt;!--*[[:Kategorie: Elektrotechnik|Elektrotechnik]]--&amp;gt;&lt;br /&gt;
*[[:Kategorie: Gestaltung|Gestaltung]]&lt;br /&gt;
*[[:Kategorie: Informatik|Informatik]]&lt;br /&gt;
*[[:Kategorie: Mathematik|Mathematik]]&lt;br /&gt;
&amp;lt;!--*[[:Kategorie: Maschinenbau|Maschinenbau]]--&amp;gt;&lt;br /&gt;
*[[:Kategorie: Wirtschaftswissenschaften|Wirtschaftswissenschaften]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&#039;&#039;&#039;Module&#039;&#039;&#039;&lt;br /&gt;
*[[:Kategorie:Modul|Modul]]&lt;br /&gt;
*[[:Kategorie:Lehrveranstaltung|Lehrveranstaltungen]]&lt;br /&gt;
&lt;br /&gt;
| style=&amp;quot;vertical-align:top; border:thin solid #dfdfdf; background-color:#fffff1;&amp;quot;  align=&amp;quot;left&amp;quot;|&lt;br /&gt;
*[[:Kategorie:Begriffsklärung|Begriffsklärungsseiten]]: Für mehrdeutige Begriffe&lt;br /&gt;
*[[:Kategorie:Glossar|Glossar]]: Die eigentlichen Glossar-Beiträge&lt;br /&gt;
*[[:Kategorie:Autor|Autor]]: Publikationslisten von Autoren&lt;br /&gt;
*[[:Kategorie:Person|Person]]: Personenbezogene Artikel&lt;br /&gt;
*[[:Kategorie:Beispiel|Beispiel]]: Beispiele zu einzelnen Definitionen&lt;br /&gt;
*[http://glossar.hs-augsburg.de/beispiel/tutorium/ Tutorien-Sourcen] (SVN-Repositories – vgl. [[Subversion]], [[Subversion/HowTo]])&lt;br /&gt;
*[[:Kategorie:Vergleich|Vergleich]]: Vergleiche von Konzepten, Definitionen etc.&lt;br /&gt;
*[[:Kategorie:Handbuch|Handbuch]]: Klassen-/Methoden-/...-Definitionen und Beschreibungen &lt;br /&gt;
*[[:Kategorie:HowTo|HowTo]]: Tipps und Tricks, kurze Anleitungen&lt;br /&gt;
*[[:Kategorie:Tool|Tool]]: Tools und Werkzeuge aller Art&lt;br /&gt;
*[[:Kategorie:Mathematischer Satz|Mathematischer Satz]]: Mathematische Sätze, Theoreme etc.&lt;br /&gt;
*[[:Kategorie:Deutsche Sprache|Deutsche Sprache]]: Rechtschreibung, Grammatik, Fremdwörter etc.&lt;br /&gt;
*[[:Kategorie:Quelle|Quelle]]: Quellenangaben zu Büchern, Aufsätzen, Internetquellen etc. &lt;br /&gt;
*[[:Kategorie:Jahr|Jahr]]: Die Ereignisse der einzelnen Jahre &lt;br /&gt;
*[[:Kategorie: {{SITENAME}}|Artikel, die {{SITENAME}} selbst betreffen]]&lt;br /&gt;
*[[:Kategorie: Formular|Alle Formulare]] (zur vereinfachten Datenerfassung)&lt;br /&gt;
*[[:Kategorie: Vorlage|Alle Vorlagen]] (= {{SITENAME}}-Templates)&lt;br /&gt;
*[[:Kategorie:Versteckte Kategorien|Versteckte Kategorien]]&lt;br /&gt;
*[[Spezial:Newpages|Neueste Artikel]]&lt;br /&gt;
*[[Spezial:Allpages|Alle Artikel]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=GlossarWiki:B%C3%BCcher/Datenmanagement/WiSe2025&amp;diff=53261</id>
		<title>GlossarWiki:Bücher/Datenmanagement/WiSe2025</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=GlossarWiki:B%C3%BCcher/Datenmanagement/WiSe2025&amp;diff=53261"/>
		<updated>2025-09-19T15:02:29Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Multimedia-Datenbanken – Skript zur Vorlesung im WiSe 2024/2025 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Multimedia-Datenbanken – Skript zur Vorlesung im WiSe 2025/2026 ===&lt;br /&gt;
Autor: [[Wolfgang Kowarschick]]&lt;br /&gt;
&lt;br /&gt;
;Praktikum&lt;br /&gt;
: [[Händler-Datenbank (SQL-Beispiel)|Die Händler-Datenbank]]&lt;br /&gt;
: Die [[Händler-Datenbank (SQL-Beispiel)/Identität|Identität]] am Beispiel der Händler-Datenbank&lt;br /&gt;
: Die [[Händler-Datenbank (SQL-Beispiel)/Projektion|Projektion]] am Beispiel der Händler-Datenbank&lt;br /&gt;
: Die [[Händler-Datenbank (SQL-Beispiel)/Selektion|Selektion]] am Beispiel der Händler-Datenbank&lt;br /&gt;
&lt;br /&gt;
;Vorlesung&lt;br /&gt;
: [[UML-Diagramme: Typische Fehler]]&lt;br /&gt;
: [[Relationale Algebra]]&lt;br /&gt;
: [[GlossarWiki:Bücher/Datenbanksysteme|Theorie]]&lt;br /&gt;
: [[:Kategorie:Datenmanagement|einschlägige Artikel im GlossarWiki]] &lt;br /&gt;
&lt;br /&gt;
;Literatur&lt;br /&gt;
: [https://glossar.hs-augsburg.de/w/media/2/2f/MMDB2018sose.pdf MMDB-Skript] (Quelle: [[Kowarschick (MMDB-Skript)]])&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Buch|Multimedia-Programmierung]]&lt;br /&gt;
[[Kategorie:Multimedia-Programmierung]]&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=GlossarWiki:B%C3%BCcher/Datenmanagement/WiSe2025&amp;diff=53259</id>
		<title>GlossarWiki:Bücher/Datenmanagement/WiSe2025</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=GlossarWiki:B%C3%BCcher/Datenmanagement/WiSe2025&amp;diff=53259"/>
		<updated>2025-09-19T15:02:03Z</updated>

		<summary type="html">&lt;p&gt;Kowa: Kowa verschob die Seite GlossarWiki:Bücher/Multimedia-Datenbanken/WiSe2024 nach GlossarWiki:Bücher/Multimedia-Datenbanken/WiSe2025&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Multimedia-Datenbanken – Skript zur Vorlesung im WiSe 2024/2025 ===&lt;br /&gt;
Autor: [[Wolfgang Kowarschick]]&lt;br /&gt;
&lt;br /&gt;
;Praktikum&lt;br /&gt;
: [[Händler-Datenbank (SQL-Beispiel)|Die Händler-Datenbank]]&lt;br /&gt;
: Die [[Händler-Datenbank (SQL-Beispiel)/Identität|Identität]] am Beispiel der Händler-Datenbank&lt;br /&gt;
: Die [[Händler-Datenbank (SQL-Beispiel)/Projektion|Projektion]] am Beispiel der Händler-Datenbank&lt;br /&gt;
: Die [[Händler-Datenbank (SQL-Beispiel)/Selektion|Selektion]] am Beispiel der Händler-Datenbank&lt;br /&gt;
&lt;br /&gt;
;Vorlesung&lt;br /&gt;
: [[UML-Diagramme: Typische Fehler]]&lt;br /&gt;
: [[Relationale Algebra]]&lt;br /&gt;
: [[GlossarWiki:Bücher/Datenbanksysteme|Theorie]]&lt;br /&gt;
: [[:Kategorie:Datenmanagement|einschlägige Artikel im GlossarWiki]] &lt;br /&gt;
&lt;br /&gt;
;Literatur&lt;br /&gt;
: [https://glossar.hs-augsburg.de/w/media/2/2f/MMDB2018sose.pdf MMDB-Skript] (Quelle: [[Kowarschick (MMDB-Skript)]])&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Buch|Multimedia-Programmierung]]&lt;br /&gt;
[[Kategorie:Multimedia-Programmierung]]&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Bachelor-Studiengang_Interaktive_Medien_(SPO_2018)&amp;diff=53258</id>
		<title>Bachelor-Studiengang Interaktive Medien (SPO 2018)</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Bachelor-Studiengang_Interaktive_Medien_(SPO_2018)&amp;diff=53258"/>
		<updated>2025-09-19T09:10:57Z</updated>

		<summary type="html">&lt;p&gt;Kowa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studiengang&lt;br /&gt;
|stgname=Interaktive Medien&lt;br /&gt;
|kuerzel=IAM (SPO 2018)&lt;br /&gt;
|abschluss=Bachelor&lt;br /&gt;
|datum=2017/12/19&lt;br /&gt;
|spo=https://www.hs-augsburg.de/Binaries/Binary41063/SPO-Interaktive-Medien-WS2018-vom-23.06.2020.pdf&lt;br /&gt;
|homepage=https://www.tha.de/ia/&lt;br /&gt;
|fakultaet=FKG, FKI&lt;br /&gt;
|studienbeginn=Wintersemester&lt;br /&gt;
|studienabschnitte=Ja&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Ein Studium im Studiengang „Interaktive Medien“ endet entweder mit dem Abschluss „Bachelor of Arts“ ({{BA}}) oder&lt;br /&gt;
mit dem Abschluss „Bachelor of Science“ ({{BSc}}). Der Studienplan der ersten beiden Semester ist unabhängig&lt;br /&gt;
vom angestrebten Abschluss. Im dritten Semester muss man sich für einen Abschluss entscheiden, da sich von diesem&lt;br /&gt;
Semester an die Studienpläne für die beiden Abschlüsse unterscheiden.&lt;br /&gt;
&lt;br /&gt;
==Studienplan SPO 2018, Studienrichtung Gestaltung ({{BA}})==&lt;br /&gt;
Den Studienplan für den Studienrichtung Informatik ({{BSc}}) finden Sie unter &amp;lt;br/&amp;gt;[[Bachelor-Studiengang_Interaktive_Medien_(SPO_2018),_B._Sc.#Studienplan_SPO_2018.2C_Studienrichtung_Informatik_.28B._Sc..29|Bachelor-Studiengang Interaktive Medien (SPO 2018), {{BSc}}]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Legende:&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;span class=&amp;quot;bgorange  white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;gestalterisch&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;lt;!-- &lt;br /&gt;
--&amp;gt;&amp;lt;span class=&amp;quot;bgblue white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;informationstechnisch&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;lt;!-- &lt;br /&gt;
--&amp;gt;&amp;lt;span class=&amp;quot;bggrey white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;übergreifend&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table class=&amp;quot;studienplan&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td rowspan=&amp;quot;3&amp;quot; class=&amp;quot;noborder&amp;quot;&amp;gt; &amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Grundlagen- und Orientierungs-&amp;lt;br/&amp;gt;phase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Spezialisierungsphase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;3&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Vertiefungsphase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;1&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;2&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;3&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;4&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;5&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;6&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;7&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;6&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Design&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen visueller Gestaltung|Grundlagen&amp;lt;br/&amp;gt;visueller Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen interaktiver Gestaltung|Grundlagen&amp;lt;br/&amp;gt;interakt. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; rowspan=&amp;quot;1&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Sprache|Sprache, 4 CP/4 SWS (AWP)]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modulkatalog:IAM (SPO 2018):Workshops und Seminare|Workshops/Sem.&amp;lt;br/&amp;gt;6 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):UX-Design|UX-Design&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Mensch und Gesellschaft|Mensch und Gesellschaft&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen interaktiver Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Theorie digitaler Medien|Theorie digitaler Medien&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Theorie --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Theorie --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- UX-Design --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mensch und Gesellschaft --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen interaktiver Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Sprache --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Theorie --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Theorie --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;8&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Praktische Tätigkeit oder Auslandsstudium|Praktische&amp;lt;br/&amp;gt;Tätigkeit&amp;lt;br/&amp;gt;oder&amp;lt;br/&amp;gt;Auslandsstudium&amp;lt;br/&amp;gt;20 CP]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;8&amp;quot; class=&amp;quot;bggrey white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Teamprojekt|Teamprojekt&amp;lt;br/&amp;gt;20 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;4&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Bachelorarbeit|Bachelorarbeit&amp;lt;br/&amp;gt;10 CP]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen dreidimensionaler Gestaltung|Grundlagen&amp;lt;br/&amp;gt;dreidim. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen audiovisueller Gestaltung|Grundlagen&amp;lt;br/&amp;gt;audiovis. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Mensch und Maschine|Mensch und&amp;lt;br/&amp;gt;Maschine&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Mensch und Maschine|Mensch und&amp;lt;br/&amp;gt;Maschine&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 3D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 4D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G-FWP --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G-FWP --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 3D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 4D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;6&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Infor-&amp;lt;br&amp;gt;matik&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Grundlagen der Informatik|Grundlagen&amp;lt;br/&amp;gt;Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Grundlagen der Webtechnologien|Grundlagen&amp;lt;br/&amp;gt;Webtech.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Raum und Zeit|Raum und&amp;lt;br/&amp;gt;Zeit&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Raum und Zeit|Raum und&amp;lt;br/&amp;gt;Zeit&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;4&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Bachelorprojekt|Bachelorprojekt&amp;lt;br/&amp;gt;8 CP/2 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Web/Netz--&amp;gt;&lt;br /&gt;
&amp;lt;!-- G/I-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G/I-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Web/Netz--&amp;gt;&lt;br /&gt;
&amp;lt;!-- G-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G/I-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen der Programmierung|Grundlagen&amp;lt;br/&amp;gt;Programmierung&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen der Softwareentwicklung|Grundlagen&amp;lt;br/&amp;gt;Softwareentw.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue  white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue  white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Software-Eng./Programmierung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- I-FWP --&amp;gt;&lt;br /&gt;
&amp;lt;!-- I-FWP --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bggrey  white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Praxisseminar|Praxisseminar&amp;lt;br/&amp;gt;2 CP/2 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Projektdurchführung|Projektdurchführung&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Kunde und Markt|Kunde und Markt&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!--  --&amp;gt;&lt;br /&gt;
&amp;lt;!--  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Kunde und Markt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;210 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;32 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;32 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;28 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;28 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Regeln===&lt;br /&gt;
* Die Prüfungen des ersten Semesters müssen spätestens im zweiten Semester erstmals absolviert werden.&lt;br /&gt;
* An Prüfungen der Vertiefungsphase kann nur teilnehmen, wer sämtliche Prüfungen der Grund- und Orientierungsphase sowie mindestens zwei Gestaltungs- und/oder Informatik-Prüfungen der Spezialisierungsphase bestanden hat.&lt;br /&gt;
* An Workshops und Seminaren kann man ebenso wie an den Sprachprüfungen der Fakultät für Natur- und Geisteswissenschaften in jedem Semester seines Studiums teilnehmen.&lt;br /&gt;
* Das Thema der Bachelorarbeit wird frühestens ausgegeben, wenn sämtlichen Prüfungen der Grund- und Orientierungsphase sowie der Spezialdsierungsphase, das Teamprojekt und die praktische Tätigkeit bzw. das Auslandssemester erfolgreich absolviert wurden. Eine Ausnahme bildes das Spachmodul: Dieses darf auch dpäter belegt werden.&lt;br /&gt;
* Das Thema des Bachelorprojektes wird gemeinsam mit dem Thema der Bachelorarbeit vergeben.&lt;br /&gt;
&lt;br /&gt;
===Wahlpflichtmodule der Spezialisierungsphase===&lt;br /&gt;
Im dritten und vierten Semester spezialisiert man sich in zweierlei Hinsicht. Zum einen wird gewählt,&lt;br /&gt;
welchen Anschluss man anstrebt, und zum anderen wählt man aus den Wahlpflichtkatalogen der &lt;br /&gt;
beiden Semester diejenigen Module, mit denen man sich thematisch vertiefend auseinandersetzen möchte.&lt;br /&gt;
&lt;br /&gt;
Es ist allerdings nicht garantiert, dass jedes Wahlpflichtmodul regelmäßig angeboten wird. &lt;br /&gt;
&lt;br /&gt;
Wenn Sie den Gestaltungsabschluss B.A. ansstreben, müssen Sie&lt;br /&gt;
aus jedem der Kataloge „Mensch und Maschine“, „Raum und Zeit“ und „Informatik“ je zwei Module absolvieren.&lt;br /&gt;
&lt;br /&gt;
[[Modulkatalog:IAM_(SPO_2018):Mensch und Maschine|&#039;&#039;&#039;Modulkatalog „Mensch und Maschine“&#039;&#039;&#039;]]&lt;br /&gt;
* Interfacedesign&lt;br /&gt;
* Interaktiondesign&lt;br /&gt;
* Physical Interfaces&lt;br /&gt;
&lt;br /&gt;
[[Modulkatalog:IAM_(SPO_2018):Raum und Zeit|&#039;&#039;&#039;Modulkatalog „Raum und Zeit“&#039;&#039;&#039;]]&lt;br /&gt;
* 2D-Animation&lt;br /&gt;
* 3D-Animation&lt;br /&gt;
* Audiovisuelles Gestalten (Klanglabor)&lt;br /&gt;
* Gamedesign (Art Design)&lt;br /&gt;
* Gamedesign (Level Design)&lt;br /&gt;
&lt;br /&gt;
[[Modulkatalog:IAM_(SPO_2018):Informatik|&#039;&#039;&#039;Modulkatalog „Informatik“&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
In jedem Semester werden Wahlpflichtveranstaltungen zu einigen der folgenden&lt;br /&gt;
Fachgebiete angeboten:&lt;br /&gt;
* Datenmanagement&lt;br /&gt;
* Web-Programmierung&lt;br /&gt;
* App-Entwicklung&lt;br /&gt;
* Netzwerke und Verteilte Systeme&lt;br /&gt;
* Spieleprogrammierung&lt;br /&gt;
* Physical Computing&lt;br /&gt;
* Computergrafik&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Bachelor-Studiengang_Interaktive_Medien_(SPO_2018),_B._Sc.&amp;diff=53257</id>
		<title>Bachelor-Studiengang Interaktive Medien (SPO 2018), B. Sc.</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Bachelor-Studiengang_Interaktive_Medien_(SPO_2018),_B._Sc.&amp;diff=53257"/>
		<updated>2025-09-19T09:10:08Z</updated>

		<summary type="html">&lt;p&gt;Kowa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studiengang&lt;br /&gt;
|stgname=Interaktive Medien&lt;br /&gt;
|kuerzel=IAM 2018 BSc&lt;br /&gt;
|abschluss=Bachelor&lt;br /&gt;
|datum=2017/12/19&lt;br /&gt;
|spo=https://www.hs-augsburg.de/Binaries/Binary41063/SPO-Interaktive-Medien-WS2018-vom-23.06.2020.pdf&lt;br /&gt;
|homepage=https://www.tha.de/ia/&lt;br /&gt;
|fakultaet=FKG, FKI&lt;br /&gt;
|studienbeginn=Wintersemester&lt;br /&gt;
|studienabschnitte=Ja&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Ein Studium gemäß der SPO 2018 endet entweder mit dem Abschluss „Bachelor of Arts“ ({{BA}}) oder&lt;br /&gt;
mit dem Abschluss „Bachelor of Science“ ({{BSc}}). Der Studienplan der ersten beiden Semester ist unabhängig&lt;br /&gt;
vom angestrebten Abschluss. Im dritten Semester muss man sich für einen Abschluss entscheiden, da sich von diesem&lt;br /&gt;
Semester an die Studienpläne für die beiden Abschlüsse unterscheiden.&lt;br /&gt;
&lt;br /&gt;
==Studienplan SPO 2018, Studienrichtung Informatik ({{BSc}})==&lt;br /&gt;
Den Studienplan für den Studienrichtung Gestaltung ({{BA}}) finden Sie unter &amp;lt;br/&amp;gt;[[Bachelor-Studiengang_Interaktive_Medien_(SPO_2018)#Studienplan_SPO_2018.2C_Studienrichtung_Gestaltung_.28B._A..29|Bachelor-Studiengang Interaktive Medien (SPO 2018), {{BA}}]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Legende:&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;span class=&amp;quot;bgorange  white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;gestalterisch&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;lt;!-- &lt;br /&gt;
--&amp;gt;&amp;lt;span class=&amp;quot;bgblue white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;informationstechnisch&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;lt;!-- &lt;br /&gt;
--&amp;gt;&amp;lt;span class=&amp;quot;bggrey white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;übergreifend&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table class=&amp;quot;studienplan&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td rowspan=&amp;quot;3&amp;quot; class=&amp;quot;noborder&amp;quot;&amp;gt; &amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Grundlagen- und Orientierungs-&amp;lt;br/&amp;gt;phase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Spezialisierungsphase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;3&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Vertiefungsphase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;1&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;2&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;3&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;4&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;5&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;6&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;7&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;6&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Design&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen visueller Gestaltung|Grundlagen&amp;lt;br/&amp;gt;visueller Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen interaktiver Gestaltung|Grundlagen&amp;lt;br/&amp;gt;interakt. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; rowspan=&amp;quot;1&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Sprache|Sprache, 4 CP/4 SWS (AWP)]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modulkatalog:IAM (SPO 2018):Workshops und Seminare|Workshops/Sem.&amp;lt;br/&amp;gt;6 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):UX-Design|UX-Design&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Mensch und Gesellschaft|Mensch und Gesellschaft&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen interaktiver Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Mathematik|Mathematik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mathematik --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mathematik --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- UX-Design --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mensch und Gesellschaft --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen interaktiver Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Sprache --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mathematik --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mathematik --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;8&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Praktische Tätigkeit oder Auslandsstudium|Praktische&amp;lt;br/&amp;gt;Tätigkeit&amp;lt;br/&amp;gt;oder&amp;lt;br/&amp;gt;Auslandsstudium&amp;lt;br/&amp;gt;20 CP]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;8&amp;quot; class=&amp;quot;bggrey white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Teamprojekt|Teamprojekt&amp;lt;br/&amp;gt;20 CP/4+4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;4&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Bachelorarbeit|Bachelorarbeit&amp;lt;br/&amp;gt;10 CP]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen dreidimensionaler Gestaltung|Grundlagen&amp;lt;br/&amp;gt;dreidim. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen audiovisueller Gestaltung|Grundlagen&amp;lt;br/&amp;gt;audiovis. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Mensch und Maschine|Mensch und&amp;lt;br/&amp;gt;Maschine&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Raum und Zeit|Raum  und&amp;lt;br/&amp;gt;Zeit&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 3D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 4D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mensche und Maschine --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Raum und Zeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 3D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 4D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mensche und Maschine --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Raum und Zeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;6&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Infor-&amp;lt;br&amp;gt;matik&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Grundlagen der Informatik|Grundlagen&amp;lt;br/&amp;gt;Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Grundlagen der Webtechnologien|Grundlagen&amp;lt;br/&amp;gt;Webtech.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;&lt;br /&gt;
[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;4&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Bachelorprojekt|Bachelorprojekt&amp;lt;br/&amp;gt;8 CP/2 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Web/Netz--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Web/Netz--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen der Programmierung|Grundlagen&amp;lt;br/&amp;gt;Programmierung&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen der Softwareentwicklung|Grundlagen&amp;lt;br/&amp;gt;Softwareentw.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue  white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue  white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Software-Eng./Programmierung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bggrey  white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Praxisseminar|Praxisseminar&amp;lt;br/&amp;gt;2 CP/2 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Projektdurchführung|Projektdurchführung&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Kunde und Markt|Kunde und Markt&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!--  --&amp;gt;&lt;br /&gt;
&amp;lt;!--  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Kunde und Markt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;210 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;32 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;32 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;28 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;28 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Regeln===&lt;br /&gt;
* Die Prüfungen des ersten Semesters müssen spätestens im zweiten Semester erstmals absolviert werden.&lt;br /&gt;
* An Prüfungen der Vertiefungsphase kann nur teilnehmen, wer sämtliche Prüfungen der Grund- und Orientierungsphase sowie mindestens zwei Gestaltungs- und/oder Informatik-Prüfungen der Spezialisierungsphase bestanden hat.&lt;br /&gt;
* An Workshops und Seminaren kann man ebenso wie an den Sprachprüfungen der Fakultät für Natur- und Geisteswissenschaften in jedem Semester seines Studiums teilnehmen.&lt;br /&gt;
* Das Thema der Bachelorarbeit wird frühestens ausgegeben, wenn sämtlichen Prüfungen der Grund- und Orientierungsphase sowie der Spezialdsierungsphase, das Teamprojekt und die praktische Tätigkeit bzw. das Auslandssemester erfolgreich absolviert wurden. Eine Ausnahme bildes das Spachmodul: Dieses darf auch dpäter belegt werden.&lt;br /&gt;
* Das Thema des Bachelorprojektes wird gemeinsam mit dem Thema der Bachelorarbeit vergeben.&lt;br /&gt;
&lt;br /&gt;
===Wahlpflichtmodule der Spezialisierungsphase===&lt;br /&gt;
Im dritten und vierten Semester spezialisiert man sich in zweierlei Hinsicht. Zum einen wird gewählt,&lt;br /&gt;
welchen Anschluss man anstrebt, und zum anderen wählt man aus den Wahlpflichtkatalogen der &lt;br /&gt;
beiden Semester diejenigen Module, mit denen man sich thematisch vertiefend auseinandersetzen möchte.&lt;br /&gt;
&lt;br /&gt;
Es ist allerdings nicht garantiert, dass jedes Wahlpflichtmodul regelmäßig angeboten wird. &lt;br /&gt;
&lt;br /&gt;
Wenn Sie den Informatikabschluss B.Sc. ansstreben, müssen Sie&lt;br /&gt;
aus dem Katalog „Informatik“ insgesamt vier Module absolvieren. &lt;br /&gt;
Aus jedem der Kataloge „Mensch und Maschine“ und „Raum und Zeit“ müssen Sie je ein Modul absolvieren.&lt;br /&gt;
&lt;br /&gt;
[[Modulkatalog:IAM_(SPO_2018):Informatik|&#039;&#039;&#039;Modulkatalog „Informatik“&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
In jedem Semester werden Wahlpflichtveranastaltungen zu einigen der folgenden&lt;br /&gt;
Fachgebiete angeboten:&lt;br /&gt;
* Datenmanagement&lt;br /&gt;
* Web-Programmierung&lt;br /&gt;
* App-Entwicklung&lt;br /&gt;
* Netzwerke und Verteilte Systeme&lt;br /&gt;
* Spieleprogrammierung&lt;br /&gt;
* Physical Computing&lt;br /&gt;
* Computergrafik&lt;br /&gt;
&lt;br /&gt;
[[Modulkatalog:IAM_(SPO_2018):Mensch und Maschine|&#039;&#039;&#039;Modulkatalog „Mensch und Maschine“&#039;&#039;&#039;]]&lt;br /&gt;
* Interfacedesign&lt;br /&gt;
* Interaktiondesign&lt;br /&gt;
* Physical Interfaces&lt;br /&gt;
&lt;br /&gt;
[[Modulkatalog:IAM_(SPO_2018):Raum und Zeit|&#039;&#039;&#039;Modulkatalog „Raum und Zeit“&#039;&#039;&#039;]]&lt;br /&gt;
* 2D-Animation&lt;br /&gt;
* 3D-Animation&lt;br /&gt;
* Audiovisuelles Gestalten (Klanglabor)&lt;br /&gt;
* Gamedesign (Art Design)&lt;br /&gt;
* Gamedesign (Level Design)&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Bachelor-Studiengang_Interaktive_Medien_(SPO_2018),_B._Sc.&amp;diff=53256</id>
		<title>Bachelor-Studiengang Interaktive Medien (SPO 2018), B. Sc.</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Bachelor-Studiengang_Interaktive_Medien_(SPO_2018),_B._Sc.&amp;diff=53256"/>
		<updated>2025-09-19T09:08:02Z</updated>

		<summary type="html">&lt;p&gt;Kowa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studiengang&lt;br /&gt;
|stgname=Interaktive Medien&lt;br /&gt;
|kuerzel=IAM 2018 BSc&lt;br /&gt;
|abschluss=Bachelor&lt;br /&gt;
|datum=2017/12/19&lt;br /&gt;
|spo=https://www.hs-augsburg.de/Binaries/Binary41063/SPO-Interaktive-Medien-WS2018-vom-23.06.2020.pdf&lt;br /&gt;
|homepage=https://www.hs-augsburg.de/interaktive-medien/iam/&lt;br /&gt;
|fakultaet=FKG, FKI&lt;br /&gt;
|studienbeginn=Wintersemester&lt;br /&gt;
|studienabschnitte=Ja&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Ein Studium gemäß der SPO 2018 endet entweder mit dem Abschluss „Bachelor of Arts“ ({{BA}}) oder&lt;br /&gt;
mit dem Abschluss „Bachelor of Science“ ({{BSc}}). Der Studienplan der ersten beiden Semester ist unabhängig&lt;br /&gt;
vom angestrebten Abschluss. Im dritten Semester muss man sich für einen Abschluss entscheiden, da sich von diesem&lt;br /&gt;
Semester an die Studienpläne für die beiden Abschlüsse unterscheiden.&lt;br /&gt;
&lt;br /&gt;
==Studienplan SPO 2018, Studienrichtung Informatik ({{BSc}})==&lt;br /&gt;
Den Studienplan für den Studienrichtung Gestaltung ({{BA}}) finden Sie unter &amp;lt;br/&amp;gt;[[Bachelor-Studiengang_Interaktive_Medien_(SPO_2018)#Studienplan_SPO_2018.2C_Studienrichtung_Gestaltung_.28B._A..29|Bachelor-Studiengang Interaktive Medien (SPO 2018), {{BA}}]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Legende:&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;span class=&amp;quot;bgorange  white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;gestalterisch&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;lt;!-- &lt;br /&gt;
--&amp;gt;&amp;lt;span class=&amp;quot;bgblue white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;informationstechnisch&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;lt;!-- &lt;br /&gt;
--&amp;gt;&amp;lt;span class=&amp;quot;bggrey white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;übergreifend&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table class=&amp;quot;studienplan&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td rowspan=&amp;quot;3&amp;quot; class=&amp;quot;noborder&amp;quot;&amp;gt; &amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Grundlagen- und Orientierungs-&amp;lt;br/&amp;gt;phase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Spezialisierungsphase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;3&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Vertiefungsphase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;1&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;2&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;3&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;4&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;5&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;6&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;7&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;6&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Design&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen visueller Gestaltung|Grundlagen&amp;lt;br/&amp;gt;visueller Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen interaktiver Gestaltung|Grundlagen&amp;lt;br/&amp;gt;interakt. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; rowspan=&amp;quot;1&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Sprache|Sprache, 4 CP/4 SWS (AWP)]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modulkatalog:IAM (SPO 2018):Workshops und Seminare|Workshops/Sem.&amp;lt;br/&amp;gt;6 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):UX-Design|UX-Design&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Mensch und Gesellschaft|Mensch und Gesellschaft&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen interaktiver Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Mathematik|Mathematik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mathematik --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mathematik --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- UX-Design --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mensch und Gesellschaft --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen interaktiver Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Sprache --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mathematik --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mathematik --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;8&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Praktische Tätigkeit oder Auslandsstudium|Praktische&amp;lt;br/&amp;gt;Tätigkeit&amp;lt;br/&amp;gt;oder&amp;lt;br/&amp;gt;Auslandsstudium&amp;lt;br/&amp;gt;20 CP]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;8&amp;quot; class=&amp;quot;bggrey white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Teamprojekt|Teamprojekt&amp;lt;br/&amp;gt;20 CP/4+4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;4&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Bachelorarbeit|Bachelorarbeit&amp;lt;br/&amp;gt;10 CP]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen dreidimensionaler Gestaltung|Grundlagen&amp;lt;br/&amp;gt;dreidim. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen audiovisueller Gestaltung|Grundlagen&amp;lt;br/&amp;gt;audiovis. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Mensch und Maschine|Mensch und&amp;lt;br/&amp;gt;Maschine&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Raum und Zeit|Raum  und&amp;lt;br/&amp;gt;Zeit&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 3D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 4D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mensche und Maschine --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Raum und Zeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 3D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 4D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mensche und Maschine --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Raum und Zeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;6&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Infor-&amp;lt;br&amp;gt;matik&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Grundlagen der Informatik|Grundlagen&amp;lt;br/&amp;gt;Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Grundlagen der Webtechnologien|Grundlagen&amp;lt;br/&amp;gt;Webtech.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;&lt;br /&gt;
[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;4&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Bachelorprojekt|Bachelorprojekt&amp;lt;br/&amp;gt;8 CP/2 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Web/Netz--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Web/Netz--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen der Programmierung|Grundlagen&amp;lt;br/&amp;gt;Programmierung&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen der Softwareentwicklung|Grundlagen&amp;lt;br/&amp;gt;Softwareentw.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue  white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue  white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Software-Eng./Programmierung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bggrey  white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Praxisseminar|Praxisseminar&amp;lt;br/&amp;gt;2 CP/2 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Projektdurchführung|Projektdurchführung&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Kunde und Markt|Kunde und Markt&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!--  --&amp;gt;&lt;br /&gt;
&amp;lt;!--  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Kunde und Markt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;210 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;32 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;32 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;28 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;28 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Regeln===&lt;br /&gt;
* Die Prüfungen des ersten Semesters müssen spätestens im zweiten Semester erstmals absolviert werden.&lt;br /&gt;
* An Prüfungen der Vertiefungsphase kann nur teilnehmen, wer sämtliche Prüfungen der Grund- und Orientierungsphase sowie mindestens zwei Gestaltungs- und/oder Informatik-Prüfungen der Spezialisierungsphase bestanden hat.&lt;br /&gt;
* An Workshops und Seminaren kann man ebenso wie an den Sprachprüfungen der Fakultät für Natur- und Geisteswissenschaften in jedem Semester seines Studiums teilnehmen.&lt;br /&gt;
* Das Thema der Bachelorarbeit wird frühestens ausgegeben, wenn sämtlichen Prüfungen der Grund- und Orientierungsphase sowie der Spezialdsierungsphase, das Teamprojekt und die praktische Tätigkeit bzw. das Auslandssemester erfolgreich absolviert wurden. Eine Ausnahme bildes das Spachmodul: Dieses darf auch dpäter belegt werden.&lt;br /&gt;
* Das Thema des Bachelorprojektes wird gemeinsam mit dem Thema der Bachelorarbeit vergeben.&lt;br /&gt;
&lt;br /&gt;
===Wahlpflichtmodule der Spezialisierungsphase===&lt;br /&gt;
Im dritten und vierten Semester spezialisiert man sich in zweierlei Hinsicht. Zum einen wird gewählt,&lt;br /&gt;
welchen Anschluss man anstrebt, und zum anderen wählt man aus den Wahlpflichtkatalogen der &lt;br /&gt;
beiden Semester diejenigen Module, mit denen man sich thematisch vertiefend auseinandersetzen möchte.&lt;br /&gt;
&lt;br /&gt;
Es ist allerdings nicht garantiert, dass jedes Wahlpflichtmodul regelmäßig angeboten wird. &lt;br /&gt;
&lt;br /&gt;
Wenn Sie den Informatikabschluss B.Sc. ansstreben, müssen Sie&lt;br /&gt;
aus dem Katalog „Informatik“ insgesamt vier Module absolvieren. &lt;br /&gt;
Aus jedem der Kataloge „Mensch und Maschine“ und „Raum und Zeit“ müssen Sie je ein Modul absolvieren.&lt;br /&gt;
&lt;br /&gt;
[[Modulkatalog:IAM_(SPO_2018):Informatik|&#039;&#039;&#039;Modulkatalog „Informatik“&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
In jedem Semester werden Wahlpflichtveranastaltungen zu einigen der folgenden&lt;br /&gt;
Fachgebiete angeboten:&lt;br /&gt;
* Datenmanagement&lt;br /&gt;
* Web-Programmierung&lt;br /&gt;
* App-Entwicklung&lt;br /&gt;
* Netzwerke und Verteilte Systeme&lt;br /&gt;
* Spieleprogrammierung&lt;br /&gt;
* Physical Computing&lt;br /&gt;
* Computergrafik&lt;br /&gt;
&lt;br /&gt;
[[Modulkatalog:IAM_(SPO_2018):Mensch und Maschine|&#039;&#039;&#039;Modulkatalog „Mensch und Maschine“&#039;&#039;&#039;]]&lt;br /&gt;
* Interfacedesign&lt;br /&gt;
* Interaktiondesign&lt;br /&gt;
* Physical Interfaces&lt;br /&gt;
&lt;br /&gt;
[[Modulkatalog:IAM_(SPO_2018):Raum und Zeit|&#039;&#039;&#039;Modulkatalog „Raum und Zeit“&#039;&#039;&#039;]]&lt;br /&gt;
* 2D-Animation&lt;br /&gt;
* 3D-Animation&lt;br /&gt;
* Audiovisuelles Gestalten (Klanglabor)&lt;br /&gt;
* Gamedesign (Art Design)&lt;br /&gt;
* Gamedesign (Level Design)&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=Bachelor-Studiengang_Interaktive_Medien_(SPO_2018)&amp;diff=53255</id>
		<title>Bachelor-Studiengang Interaktive Medien (SPO 2018)</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=Bachelor-Studiengang_Interaktive_Medien_(SPO_2018)&amp;diff=53255"/>
		<updated>2025-09-19T09:07:12Z</updated>

		<summary type="html">&lt;p&gt;Kowa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studiengang&lt;br /&gt;
|stgname=Interaktive Medien&lt;br /&gt;
|kuerzel=IAM (SPO 2018)&lt;br /&gt;
|abschluss=Bachelor&lt;br /&gt;
|datum=2017/12/19&lt;br /&gt;
|spo=https://www.hs-augsburg.de/Binaries/Binary41063/SPO-Interaktive-Medien-WS2018-vom-23.06.2020.pdf&lt;br /&gt;
|homepage=https://www.hs-augsburg.de/interaktive-medien/iam/&lt;br /&gt;
|fakultaet=FKG, FKI&lt;br /&gt;
|studienbeginn=Wintersemester&lt;br /&gt;
|studienabschnitte=Ja&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Ein Studium im Studiengang „Interaktive Medien“ endet entweder mit dem Abschluss „Bachelor of Arts“ ({{BA}}) oder&lt;br /&gt;
mit dem Abschluss „Bachelor of Science“ ({{BSc}}). Der Studienplan der ersten beiden Semester ist unabhängig&lt;br /&gt;
vom angestrebten Abschluss. Im dritten Semester muss man sich für einen Abschluss entscheiden, da sich von diesem&lt;br /&gt;
Semester an die Studienpläne für die beiden Abschlüsse unterscheiden.&lt;br /&gt;
&lt;br /&gt;
==Studienplan SPO 2018, Studienrichtung Gestaltung ({{BA}})==&lt;br /&gt;
Den Studienplan für den Studienrichtung Informatik ({{BSc}}) finden Sie unter &amp;lt;br/&amp;gt;[[Bachelor-Studiengang_Interaktive_Medien_(SPO_2018),_B._Sc.#Studienplan_SPO_2018.2C_Studienrichtung_Informatik_.28B._Sc..29|Bachelor-Studiengang Interaktive Medien (SPO 2018), {{BSc}}]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Legende:&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;span class=&amp;quot;bgorange  white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;gestalterisch&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;lt;!-- &lt;br /&gt;
--&amp;gt;&amp;lt;span class=&amp;quot;bgblue white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;informationstechnisch&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;lt;!-- &lt;br /&gt;
--&amp;gt;&amp;lt;span class=&amp;quot;bggrey white&amp;quot;&amp;gt;&amp;amp;nbsp;&#039;&#039;&#039;übergreifend&#039;&#039;&#039;&amp;amp;nbsp;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table class=&amp;quot;studienplan&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td rowspan=&amp;quot;3&amp;quot; class=&amp;quot;noborder&amp;quot;&amp;gt; &amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Grundlagen- und Orientierungs-&amp;lt;br/&amp;gt;phase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Spezialisierungsphase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;3&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Vertiefungsphase&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Sommer&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Winter&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;1&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;2&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;3&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;4&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;5&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;6&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; class=&amp;quot;bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;7&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;6&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Design&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen visueller Gestaltung|Grundlagen&amp;lt;br/&amp;gt;visueller Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen interaktiver Gestaltung|Grundlagen&amp;lt;br/&amp;gt;interakt. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; rowspan=&amp;quot;1&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Sprache|Sprache, 4 CP/4 SWS (AWP)]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modulkatalog:IAM (SPO 2018):Workshops und Seminare|Workshops/Sem.&amp;lt;br/&amp;gt;6 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):UX-Design|UX-Design&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Mensch und Gesellschaft|Mensch und Gesellschaft&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen interaktiver Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;2&amp;quot; rowspan=&amp;quot;2&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Theorie digitaler Medien|Theorie digitaler Medien&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Theorie --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Theorie --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- UX-Design --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Mensch und Gesellschaft --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen interaktiver Gestaltung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Sprache --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Theorie --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Theorie --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;8&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Praktische Tätigkeit oder Auslandsstudium|Praktische&amp;lt;br/&amp;gt;Tätigkeit&amp;lt;br/&amp;gt;oder&amp;lt;br/&amp;gt;Auslandsstudium&amp;lt;br/&amp;gt;20 CP]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;8&amp;quot; class=&amp;quot;bggrey white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Teamprojekt|Teamprojekt&amp;lt;br/&amp;gt;20 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;4&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Bachelorarbeit|Bachelorarbeit&amp;lt;br/&amp;gt;10 CP]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen dreidimensionaler Gestaltung|Grundlagen&amp;lt;br/&amp;gt;dreidim. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot;  class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen audiovisueller Gestaltung|Grundlagen&amp;lt;br/&amp;gt;audiovis. Gest.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Mensch und Maschine|Mensch und&amp;lt;br/&amp;gt;Maschine&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Mensch und Maschine|Mensch und&amp;lt;br/&amp;gt;Maschine&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 3D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 4D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G-FWP --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G-FWP --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Design  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 3D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Einstieg 4D --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorarbeit --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;6&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Infor-&amp;lt;br&amp;gt;matik&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Grundlagen der Informatik|Grundlagen&amp;lt;br/&amp;gt;Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Grundlagen der Webtechnologien|Grundlagen&amp;lt;br/&amp;gt;Webtech.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Raum und Zeit|Raum und&amp;lt;br/&amp;gt;Zeit&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Raum und Zeit|Raum und&amp;lt;br/&amp;gt;Zeit&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;4&amp;quot; class=&amp;quot;bgorange white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Bachelorprojekt|Bachelorprojekt&amp;lt;br/&amp;gt;8 CP/2 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Web/Netz--&amp;gt;&lt;br /&gt;
&amp;lt;!-- G/I-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G/I-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung--&amp;gt;&lt;br /&gt;
&amp;lt;!-- Web/Netz--&amp;gt;&lt;br /&gt;
&amp;lt;!-- G-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- G/I-FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Teamprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen der Programmierung|Grundlagen&amp;lt;br/&amp;gt;Programmierung&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM_(SPO_2018):Grundlagen der Softwareentwicklung|Grundlagen&amp;lt;br/&amp;gt;Softwareentw.&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue  white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;3&amp;quot; class=&amp;quot;bgblue  white&amp;quot;&amp;gt;[[Modulkatalog:IAM_(SPO_2018):Informatik|Informatik&amp;lt;br/&amp;gt;8 CP/6 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Bachelorprojekt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Grundlagen Programmierung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Software-Eng./Programmierung --&amp;gt;&lt;br /&gt;
&amp;lt;!-- I-FWP --&amp;gt;&lt;br /&gt;
&amp;lt;!-- I-FWP --&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bggrey  white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Praxisseminar|Praxisseminar&amp;lt;br/&amp;gt;2 CP/2 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bgblue white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Projektdurchführung|Projektdurchführung&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td colspan=&amp;quot;1&amp;quot; rowspan=&amp;quot;2&amp;quot; class=&amp;quot;bggrey   white&amp;quot;&amp;gt;[[Modul:IAM (SPO 2018):Kunde und Markt|Kunde und Markt&amp;lt;br/&amp;gt;5 CP/4 SWS]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tr class=&amp;quot;mw-line-own&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- Informatik  --&amp;gt;&lt;br /&gt;
&amp;lt;!--  --&amp;gt;&lt;br /&gt;
&amp;lt;!--  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- FWP  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Praktische Tätigkeit --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Medienprojekt  --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Kunde und Markt --&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;210 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;32 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;32 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;30 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;28 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td class=&amp;quot;top bgdgrey white&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;28 CP&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Regeln===&lt;br /&gt;
* Die Prüfungen des ersten Semesters müssen spätestens im zweiten Semester erstmals absolviert werden.&lt;br /&gt;
* An Prüfungen der Vertiefungsphase kann nur teilnehmen, wer sämtliche Prüfungen der Grund- und Orientierungsphase sowie mindestens zwei Gestaltungs- und/oder Informatik-Prüfungen der Spezialisierungsphase bestanden hat.&lt;br /&gt;
* An Workshops und Seminaren kann man ebenso wie an den Sprachprüfungen der Fakultät für Natur- und Geisteswissenschaften in jedem Semester seines Studiums teilnehmen.&lt;br /&gt;
* Das Thema der Bachelorarbeit wird frühestens ausgegeben, wenn sämtlichen Prüfungen der Grund- und Orientierungsphase sowie der Spezialdsierungsphase, das Teamprojekt und die praktische Tätigkeit bzw. das Auslandssemester erfolgreich absolviert wurden. Eine Ausnahme bildes das Spachmodul: Dieses darf auch dpäter belegt werden.&lt;br /&gt;
* Das Thema des Bachelorprojektes wird gemeinsam mit dem Thema der Bachelorarbeit vergeben.&lt;br /&gt;
&lt;br /&gt;
===Wahlpflichtmodule der Spezialisierungsphase===&lt;br /&gt;
Im dritten und vierten Semester spezialisiert man sich in zweierlei Hinsicht. Zum einen wird gewählt,&lt;br /&gt;
welchen Anschluss man anstrebt, und zum anderen wählt man aus den Wahlpflichtkatalogen der &lt;br /&gt;
beiden Semester diejenigen Module, mit denen man sich thematisch vertiefend auseinandersetzen möchte.&lt;br /&gt;
&lt;br /&gt;
Es ist allerdings nicht garantiert, dass jedes Wahlpflichtmodul regelmäßig angeboten wird. &lt;br /&gt;
&lt;br /&gt;
Wenn Sie den Gestaltungsabschluss B.A. ansstreben, müssen Sie&lt;br /&gt;
aus jedem der Kataloge „Mensch und Maschine“, „Raum und Zeit“ und „Informatik“ je zwei Module absolvieren.&lt;br /&gt;
&lt;br /&gt;
[[Modulkatalog:IAM_(SPO_2018):Mensch und Maschine|&#039;&#039;&#039;Modulkatalog „Mensch und Maschine“&#039;&#039;&#039;]]&lt;br /&gt;
* Interfacedesign&lt;br /&gt;
* Interaktiondesign&lt;br /&gt;
* Physical Interfaces&lt;br /&gt;
&lt;br /&gt;
[[Modulkatalog:IAM_(SPO_2018):Raum und Zeit|&#039;&#039;&#039;Modulkatalog „Raum und Zeit“&#039;&#039;&#039;]]&lt;br /&gt;
* 2D-Animation&lt;br /&gt;
* 3D-Animation&lt;br /&gt;
* Audiovisuelles Gestalten (Klanglabor)&lt;br /&gt;
* Gamedesign (Art Design)&lt;br /&gt;
* Gamedesign (Level Design)&lt;br /&gt;
&lt;br /&gt;
[[Modulkatalog:IAM_(SPO_2018):Informatik|&#039;&#039;&#039;Modulkatalog „Informatik“&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
In jedem Semester werden Wahlpflichtveranstaltungen zu einigen der folgenden&lt;br /&gt;
Fachgebiete angeboten:&lt;br /&gt;
* Datenmanagement&lt;br /&gt;
* Web-Programmierung&lt;br /&gt;
* App-Entwicklung&lt;br /&gt;
* Netzwerke und Verteilte Systeme&lt;br /&gt;
* Spieleprogrammierung&lt;br /&gt;
* Physical Computing&lt;br /&gt;
* Computergrafik&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53254</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53254"/>
		<updated>2025-05-23T10:38:42Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* src/server.js */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05a erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     # Wechsle in den Branch v05b&lt;br /&gt;
git checkout -b v05c  # Klone v05b in einen neuen Branch v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den I18N-Store werden folgende Funktionen ergänzt:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem.&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Inhalt von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; vereinfacht sich entsprechend.&lt;br /&gt;
Diese Datei nur noch Funktionen zur Verfügung, der in der Phrase &amp;lt;code&amp;gt;phrases.hello&amp;lt;/code&amp;gt;&lt;br /&gt;
den String &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt; durch einen Namen ersetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }             from &#039;pinia&#039;&lt;br /&gt;
import { ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
import StoreI18n                   from &#039;./StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
const storeGreeting =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;greeting&#039;,&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      i18n =&lt;br /&gt;
        StoreI18n(),&lt;br /&gt;
&lt;br /&gt;
      phrases =&lt;br /&gt;
        i18n.phrases,&lt;br /&gt;
&lt;br /&gt;
      dictionary =&lt;br /&gt;
        i18n.dictionary,&lt;br /&gt;
&lt;br /&gt;
      f_hello =&lt;br /&gt;
        p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Initiaisierung durch &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden, da das asynchrone Laden von Initialisierungsdaten in jedem Store selbst geregelt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung der einzelnen Stores erfolgt in der Hauptdatei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeI18n    from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
  &lt;br /&gt;
  // Activate initializer of &amp;quot;storeI18n&amp;quot;&lt;br /&gt;
  storeI18n(); // First call of storeI18n(): The code of the function is performed&lt;br /&gt;
               // All other calls: The reult of the first call is returned (Singleton)&lt;br /&gt;
&lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05c) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
=== express_hello_world ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Backend-Server mittels Express ===&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is running on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_06&amp;diff=53253</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 06</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_06&amp;diff=53253"/>
		<updated>2025-05-23T09:54:22Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Hello World Vue 06b */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v06&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v06a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v06b&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
Die Version des [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05|fünften Teils des Vue-Tutoriums]]&lt;br /&gt;
wird so angepasst, dass die Sprache zur Laufzeit geändert werden kann. &lt;br /&gt;
&lt;br /&gt;
Zunächst wird jedoch eine Navigationskomponente eingeführt. Dies hat den Zweck, das Routing an einem Beispiel zu erklären.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05d    # Wechsle in den Branch v05d mit Express-Server&lt;br /&gt;
git checkout -b v06  # Klone v05d in einen neuen Branch v06&lt;br /&gt;
rm -rf express_Hello_world&lt;br /&gt;
cd frontend&lt;br /&gt;
npm i&lt;br /&gt;
npm i vue-router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;public/json/config.jso&amp;lt;/code&amp;gt;&lt;br /&gt;
aktivieren wir wieder den Zugriff auf die lokalen JSON-Dateien.&lt;br /&gt;
Den Zugriff auf den Backend-Server, um die Bilder von dort zu holen, behandeln wir später.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fügen Sie außerdem in die Datei &amp;lt;code&amp;gt;vite.config.common.js&amp;lt;/code&amp;gt; zwei geeignete Aliase für &lt;br /&gt;
&amp;lt;code&amp;gt;view&amp;lt;/code&amp;gt; und &amp;lt;/code&amp;gt;router&amp;lt;/code&amp;gt; ein.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Anstelle von Sections werden im Frontend Views verwendet.&lt;br /&gt;
Dazu werden die Section-Komponenten in den Ordner View verschoben und umbenannt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
src/view/ViewHello.vue&lt;br /&gt;
src/view/ViewHome.vue     // an Stelle von ViewForm.vue&lt;br /&gt;
&lt;br /&gt;
src/view/ViewError404.vue // neu&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die CSS-Dateien müssen natürlich auch entsprechend verschoben und abgepasst werden.&lt;br /&gt;
&lt;br /&gt;
ViewForm.vue wird in ViewHome.vue unbenannt,&lt;br /&gt;
da diese Seite als Startseite verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird eine Seite ViewError404.vue angelegt, die immer dann ausgeliefert wird, wenn der Benutzer auf eine URL zugreift, der keine View-Datei zugeordnet ist.&lt;br /&gt;
&lt;br /&gt;
Die CSS-Dateien sollten analog umbenannt und unter den neuen Namen importiert werden. Für &amp;lt;code&amp;gt;ViewError404.vue&amp;lt;/code&amp;gt; muss natürlich auch eine CSS-Datei angelegt werden. Diese verweist allerdings nur auf &amp;lt;code&amp;gt;View.css&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/view/ViewError404.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import router    from &#039;/router&#039;&lt;br /&gt;
  import storeI18n from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const phrases = storeI18n().phrases&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;h1&amp;gt;404&amp;lt;/h1&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;{{phrases.pageNotFound.replace(&#039;$1&#039;, router.currentRoute.value.path)}}&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/view/ViewError404.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Router-Plugin ===&lt;br /&gt;
&lt;br /&gt;
Um mit Vue Multipage-Anwendungen realisieren zu können, benötigt man einRouter-Plugin.&lt;br /&gt;
Dieses muss  zunächst händisch auf Basis von &amp;lt;code&amp;gt;vue-router&amp;lt;/code&amp;gt; implementiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/router/index.js&lt;br /&gt;
&lt;br /&gt;
import { createRouter, createWebHistory } from &#039;vue-router&#039;&lt;br /&gt;
import ViewHome                           from &#039;/view/ViewHome.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  routes =&lt;br /&gt;
    [ { path: &#039;/&#039;,&lt;br /&gt;
        name: &#039;home&#039;,&lt;br /&gt;
        component: ViewHome,&lt;br /&gt;
      },&lt;br /&gt;
      { path: &#039;/hello&#039;,&lt;br /&gt;
        name: &#039;hello&#039;,&lt;br /&gt;
        component: () =&amp;gt; import(&#039;/view/ViewHello.vue&#039;),&lt;br /&gt;
      },&lt;br /&gt;
      { path:      &#039;/:pathMatch(.*)&#039;,&lt;br /&gt;
        component: () =&amp;gt; import(&#039;/view/ViewError404.vue&#039;),&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
&lt;br /&gt;
  router =&lt;br /&gt;
    createRouter({ history: createWebHistory(), routes })&lt;br /&gt;
&lt;br /&gt;
export default router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun muss man das neu erstellte Plugin in die App integrieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import router          from &#039;./router&#039;  // &#039;./router/index.js&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
const&lt;br /&gt;
  pinia   = createPinia(),&lt;br /&gt;
  app     = createApp(App),&lt;br /&gt;
  init    = async () =&amp;gt;&lt;br /&gt;
            { app&lt;br /&gt;
                .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
                .use(pinia)&lt;br /&gt;
                .use(router)&lt;br /&gt;
                .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== index.html ===&lt;br /&gt;
&lt;br /&gt;
Da das &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;-Tag nun innerhalb von &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; verwendet werden soll, &lt;br /&gt;
wird es aus der Datei &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; entfernt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  &amp;lt;body id=&amp;quot;app&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== App.vue ===&lt;br /&gt;
&lt;br /&gt;
Nun kann in die App eine Navigation eingebaut werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- /src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import AppNav       from &#039;/component/AppNav.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeI18n    from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  storeI18n();&lt;br /&gt;
&lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p      v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;AppNav v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;main   v-if=&amp;quot;session.isInitialized&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;router-view/&amp;gt;&lt;br /&gt;
  &amp;lt;/main&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
  @import &#039;/css/body.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zugehörige Navigationskomponente muss auch noch implementiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- /src/component/AppNav.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;nav&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/&amp;quot;&amp;gt;Home&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/hello&amp;quot;&amp;gt;Hello&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
  &amp;lt;/nav&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/AppNav.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* src/css/_config.css */&lt;br /&gt;
...&lt;br /&gt;
--background-color:  #C5EFFC;&lt;br /&gt;
--link-color:        #0027d4;&lt;br /&gt;
...&lt;br /&gt;
--nav-dist:          0.5em;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* src/css/AppNav.css */&lt;br /&gt;
&lt;br /&gt;
@import &#039;config.css&#039;;&lt;br /&gt;
&lt;br /&gt;
nav&lt;br /&gt;
{ top:      0;&lt;br /&gt;
  width:    100%;&lt;br /&gt;
  position: fixed;&lt;br /&gt;
&lt;br /&gt;
  ul&lt;br /&gt;
  { list-style-type: none;&lt;br /&gt;
    margin: 0 var(--nav-dist);&lt;br /&gt;
    padding: 0;&lt;br /&gt;
&lt;br /&gt;
    li&lt;br /&gt;
    { display: inline;&lt;br /&gt;
      a { padding: 0 var(--nav-dist); }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    a&lt;br /&gt;
    { text-decoration: none;&lt;br /&gt;
      color:           var(--link-color);&lt;br /&gt;
      font-weight:     bold;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    a:hover, .router-link-exact-active&lt;br /&gt;
    { text-decoration: underline;&lt;br /&gt;
      color:           var(--link-color);&lt;br /&gt;
      font-weight:     bold;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  label, input&lt;br /&gt;
  { margin: 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Navigation ===&lt;br /&gt;
Die Navigation muss in allen Komponenten, in den Seiten gewechselt werden, angepasst werden.&lt;br /&gt;
&lt;br /&gt;
Das ist in der App.vue der Fall (siehe zuvor) sowie in der ViewHome.vue.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- /src/vie/ViewHome.vue  --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
  import router        from &#039;/router&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    sayHello   = () =&amp;gt; router.push(&#039;/hello&#039;)&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Section-Verwaltung der ehemaligen Single-Page-Anwendung kann und sollte gelöscht werden:&lt;br /&gt;
* &amp;lt;code&amp;gt;config.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;storeSession.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ViewHome.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ViewHello.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; kann (samt CSS-Datei) ganz gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06a ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die Ausgabesprache soll vom Benutzer zur Laufzeit geändert werden können. Die Sprachen sollen durch Bilder (Flaggen) symbolisiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v06      # Wechsle in den Branch v06&lt;br /&gt;
git checkout -b v06a  # Klone v06 in einen neuen Branch v06a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Um die vorhandenen Sprachen durchblättern zu können, wird ein einfacher Automat verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
  &amp;quot;nextLanguage&amp;quot;:    {&amp;quot;de&amp;quot;: &amp;quot;en&amp;quot;, &amp;quot;en&amp;quot;: &amp;quot;de&amp;quot;}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== StoreI18n.js: Integration von nextLanguage ===&lt;br /&gt;
&lt;br /&gt;
Der Automat &amp;lt;code&amp;gt;nextLanguage&amp;lt;/code&amp;gt; wird in den StoreI18n integriert. Wenn die Methode &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; ohne Argument aufgerufen wird, soll der Automat die aktuelle Sprache &amp;lt;code&amp;gt;lang&amp;lt;/code&amp;gt; durch die Nachfolgersprache ersetzen.&lt;br /&gt;
Dazu sollte zunächst die zunächst Konstante &amp;lt;code&amp;gt;nextLanguage&amp;lt;/code&amp;gt; definiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
lang =&lt;br /&gt;
  ref(null),&lt;br /&gt;
&lt;br /&gt;
nextLanguage =&lt;br /&gt;
  reactive(config.nextLanguage),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese neue Konstante &amp;lt;code&amp;gt;nextLanguage&amp;lt;/code&amp;gt; wird verwendet, um die Sprache mittels &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; zu definieren, sofern&lt;br /&gt;
kein Argument übergeben wurde.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; soll bei einer Änderung der Sprache sowohl die notwendige JSON-Sprach-Datei laden als auch die in der HTML-Datei &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; ausgewiesene Sprache anpassen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/storeI18N.js&lt;br /&gt;
&lt;br /&gt;
changeLang =&lt;br /&gt;
  async (p_lang = null) =&amp;gt;&lt;br /&gt;
        { lang.value = p_lang ?? nextLanguage[lang.value];&lt;br /&gt;
          await getI18n(lang.value);&lt;br /&gt;
          document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
initialize = &lt;br /&gt;
  async resolve =&amp;gt; &lt;br /&gt;
        { await changeLang(config.defaultLanguage); &lt;br /&gt;
          resolve(); &lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bilder von Flaggen ===&lt;br /&gt;
&lt;br /&gt;
Zur Visualisierung der gewählten Sprache werden Bilder von Flaggen eingesetzt.&lt;br /&gt;
Wir kopieren die Bilder im SVG-Format aus dem NPM-Paket &amp;quot;https://github.com/lipis/flag-icons&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D flag-icons&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Ordner &amp;quot;node_modules/flag-icons/flags/4x3&amp;quot; finden Sie Flaggen zahlreicher Länder im 4-zu-3-Format.&lt;br /&gt;
Kopieren Sie die Flaggen der Länder Deutschland und Großbritanien in den Ordner &amp;lt;code&amp;gt;public/img&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In die Datei config.js wird der Pfad, unter dem die Bilder zu finden sind, eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
//   public/config.js&lt;br /&gt;
...&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:  &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;imgPath&amp;quot;:  &amp;quot;/img/$1.svg&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;: &amp;quot;/api/$1&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;FormButtonIcon&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Komponente wird benötigt: FormButtonIcon&lt;br /&gt;
Dabei handelt es sich um einen Button, der ein Icon anzeigt.&lt;br /&gt;
Das Bild kann zur Laufzeit geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import { computed, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    props       = defineProps&lt;br /&gt;
                  ({ image: { type: String },&lt;br /&gt;
                     width: { type: String, default: &#039;3em&#039;},&lt;br /&gt;
                  }),&lt;br /&gt;
    config      = inject(&#039;config&#039;),&lt;br /&gt;
    emit        = defineEmits([&#039;click&#039;]),&lt;br /&gt;
    click       = () =&amp;gt; emit(&#039;click&#039;),&lt;br /&gt;
    require     = v_url =&amp;gt; import.meta.url == null&lt;br /&gt;
                           ? v_url&lt;br /&gt;
                           : new URL(v_url, import.meta.url).href,&lt;br /&gt;
    imgPath     = config.imgPath,&lt;br /&gt;
    buttonImage = computed(() =&amp;gt; require(imgPath.replace(&#039;$1&#039;, props.image)))&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;input type=&amp;quot;image&amp;quot; :src=&amp;quot;buttonImage&amp;quot; :style=&amp;quot;{width}&amp;quot; @click=&amp;quot;click&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/form/FormButtonIcon.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
/* src/css/component/FormButtonIcon.css */&lt;br /&gt;
&lt;br /&gt;
@import &#039;FormButton.css&#039;;&lt;br /&gt;
&lt;br /&gt;
input&lt;br /&gt;
{ width:       $button_icon_width !important;&lt;br /&gt;
  margin-left: $nav-dist          !important;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fügen Sie in &amp;lt;code&amp;gt;src/css/_config.css&amp;lt;/code&amp;gt; noch &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
--button-icon-width: 1em;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ein.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterung von &amp;lt;code&amp;gt;AppNav.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Sprachwahl-Button muss noch in die Navigation eingebaut werden. In diesem Rahmen wird auch gleich die Navigation etwas internationalisiert.&lt;br /&gt;
&lt;br /&gt;
Ergänzen Sie in den Dictionaries der Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt;&lt;br /&gt;
folgende Attribute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
    ...         ...,&lt;br /&gt;
    &amp;quot;navHome&amp;quot;:  &amp;quot;Start&amp;quot;,&lt;br /&gt;
    &amp;quot;navHello&amp;quot;: &amp;quot;Hallo&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
    ...         ...,&lt;br /&gt;
    &amp;quot;navHome&amp;quot;:  &amp;quot;Home&amp;quot;,&lt;br /&gt;
    &amp;quot;navHello&amp;quot;: &amp;quot;Hello&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können Sie das Navigationsmenü internationalisieren (einschließlich Auswahl der Sprache per Button).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/component/AppNav.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButtonIcon from &#039;/component/form/FormButtonIcon.vue&#039;&lt;br /&gt;
  import storeI18n      from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const i18n = storeI18n()&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;nav&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/&amp;quot;&amp;gt;{{i18n.dictionary.navHome}}&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/hello&amp;quot;&amp;gt;{{i18n.dictionary.navHello}}&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;FormButtonIcon :image=&amp;quot;i18n.lang&amp;quot; @click=&amp;quot;i18n.changeLang()&amp;quot;/&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
  &amp;lt;/nav&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/AppNav.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06b ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Sorgen Sie dafür, dass &amp;lt;code&amp;gt;autofocus&amp;lt;/code&amp;gt; korrekt funktioniert, insbesondere beim Seitenwechsel via Navigation.&lt;br /&gt;
&lt;br /&gt;
Es ist sinnvoll, die Funktionalität in ein eigenes Store-Modul auszulagern, damit&lt;br /&gt;
das Session-Modul in Anwendungen, die keine Autofocus-Funktionalität benötigen, keinen&lt;br /&gt;
überflüssigen Code enthalten (Bertrand Meyer: Jedes Modul erfüllt genau eine Aufgabe).&lt;br /&gt;
Das Session-Modul wird entsprechend abgespeckt.&lt;br /&gt;
 &lt;br /&gt;
Der Autofocus-Store soll zwei Funktionen bieten:&lt;br /&gt;
* &amp;lt;code&amp;gt;setAutofocusId&amp;lt;/code&amp;gt;: Speichert die ID des Autofokus-Elements der aktuellen Seite (Default: &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;lt;code&amp;gt;setFocus&amp;lt;/code&amp;gt;: Setzt den Focus auf die aktuelle Autofokus-Komponente (sofern die ID ungleich &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; ist). Insbesondere für das Window-Ereignis &amp;lt;code&amp;gt;&#039;visibilitychange&#039;&amp;lt;/code&amp;gt; soll diese Funktion&lt;br /&gt;
aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/StoreAutofocus.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore } from &#039;pinia&#039;&lt;br /&gt;
import { ref }         from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeAutofocus =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;autofocus&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      c_autofocus_id = ref(),&lt;br /&gt;
      setFocus       = () =&amp;gt; document.getElementById(c_autofocus_id.value)?.focus(),&lt;br /&gt;
      setAutofocusId = p_autofocus_id =&amp;gt; { c_autofocus_id.value = p_autofocus_id; &lt;br /&gt;
                                           setFocus();&lt;br /&gt;
                                         }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;visibilitychange&#039;, setFocus);&lt;br /&gt;
&lt;br /&gt;
    return { setAutofocusId, setFocus }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeAutofocus&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore } from &#039;pinia&#039;&lt;br /&gt;
import { ref }         from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die aktuelle ID speichern zu können, muss das zugehörige Textfeld die Information, welche ID es hat, &lt;br /&gt;
per &amp;lt;code&amp;gt;emit&amp;lt;/code&amp;gt; an die Elternkomponente ausgeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
// /src/component/form/FormTextfield.vue&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
const&lt;br /&gt;
  props = defineProps&lt;br /&gt;
          ({ id: { type: String,  default: &#039;&#039; },&lt;br /&gt;
             ...&lt;br /&gt;
          })&lt;br /&gt;
  ... &lt;br /&gt;
  emit  = defineEmits([&#039;id&#039;, &#039;update:text&#039;, &#039;enter&#039;]),&lt;br /&gt;
...&lt;br /&gt;
emit(&#039;id&#039;, inputId);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen die Views, die ein Autofokus-Feld beinhalten, dieses an den Session-Store melden, &lt;br /&gt;
sobald sie per Routing aktiviert werden. Das ist in unserem Fall nur die &amp;lt;code&amp;gt;ViewHome&amp;lt;/code&amp;gt;.&lt;br /&gt;
Ein Watcher sorgt dafür, dass die ID des Textfeldes im Session-Objekt gespeichert wird, sobald &lt;br /&gt;
das zugehörige Textfeld die ID per &amp;lt;code&amp;gt;emit&amp;lt;/code&amp;gt; meldet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
// /src/view/ViewHome.vue&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import { ref, watch } from &#039;vue&#039;&lt;br /&gt;
  ...&lt;br /&gt;
  import storeAutofocus from &#039;/store/StoreAutofocus&#039;&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    id         = ref(null),&lt;br /&gt;
    autofocus  = storeAutofocus(),&lt;br /&gt;
    ...&lt;br /&gt;
    setId      = p_id =&amp;gt; id.value = p_id,&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
  watch( id, () =&amp;gt; autofocus.setAutofocusId(id.value) )&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
        &amp;lt;FormTextfield @id=&amp;quot;setId&amp;quot;&lt;br /&gt;
          ...&lt;br /&gt;
        &amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schluss muss im Router noch die Autofocus-ID bei jedem Seitenwechsel gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/router/index.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import storeAutofocus from &#039;/store/StoreAutofocus&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
router.beforeEach(() =&amp;gt; storeAutofocus().setAutofocusId())&lt;br /&gt;
&lt;br /&gt;
export default router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist hier, dass man keine Konstante &amp;lt;code&amp;gt;session&amp;lt;/code&amp;gt; definieren kann, da die Stores erst nach dem Router-Objekt erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/router/index.js&lt;br /&gt;
...&lt;br /&gt;
import storeAutofocus from &#039;/store/StoreAutofocus&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  autofocus = storeAutofocus(), // FUNKTIONIERT NICHT&lt;br /&gt;
  ...&lt;br /&gt;
router.beforeEach(() =&amp;gt; autofocus.setAutofocusId())&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Funktionsaufruf &amp;lt;code&amp;gt;storeSession()&amp;lt;/code&amp;gt; darf daher erst in  der Callback-Funktion von &lt;br /&gt;
&amp;lt;code&amp;gt;router.beforeEach&amp;lt;/code&amp;gt; aufgerufen werden. Zu diesem Zeitpunkt sind die Stores und der Router auf jeden Fall schon initialisiert worden.&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss sollten Sie noch versuchen, &amp;lt;code&amp;gt;/src/component/AppNav.vue&amp;lt;/code&amp;gt; so abzuändern,&lt;br /&gt;
dass ein Klick auf die Fahne nicht nur die Sprache ändert, sondern auch noch den Focus auf das Startfeld setzt.&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06c ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Sorgen Sie dafür, dass die unterstützten Sprachen und der Sprach-Automat vom Backend&lt;br /&gt;
geladen wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v06b     # Wechsle in den Branch v06b&lt;br /&gt;
git checkout -b v06c  # Klone v06b in einen neuen Branch v06c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Splitten Sie die Config-Datei zunächst in zwei Teile: &amp;quot;config.json&amp;quot; wird aus dem Public-Ordner geladen.&lt;br /&gt;
&amp;quot;config_backend.json&amp;quot; wird normalerweise vom Backend geladen, kann aber zum Entwicklungszeitpunkt auch vom Public-Ordner geladen werden, wenn das Backend noch nicht stabil läuft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;configBackend&amp;quot;:  &amp;quot;/json/config_backend.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;imgPath&amp;quot;:        &amp;quot;/img/$1.svg&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:       &amp;quot;/api/$1&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/public/json/config_backend.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;languages&amp;quot;:    [&amp;quot;de&amp;quot;, &amp;quot;en&amp;quot;],&lt;br /&gt;
  &amp;quot;nextLanguage&amp;quot;: {&amp;quot;de&amp;quot;: &amp;quot;en&amp;quot;, &amp;quot;en&amp;quot;: &amp;quot;de&amp;quot;}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Backend teilt dem Frontend mit, welche Sprachen unterstützt werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Laden Sie, nachdem Sie &amp;quot;config&amp;quot; asynchron geladen haben, &amp;quot;configBackend&amp;quot; asynchron und kopieren Sie den Inhalt dieses Objekts in das Config-Objekt. Den Pfad von &amp;quot;configBackend&amp;quot; finden Sie unter &amp;quot;config.configBackend&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
const&lt;br /&gt;
  ...&lt;br /&gt;
  init = async () =&amp;gt;&lt;br /&gt;
         { const&lt;br /&gt;
             config        = await getJson(&#039;/json/config.json&#039;),&lt;br /&gt;
             configBackend = await getJson(config.configBackend)&lt;br /&gt;
&lt;br /&gt;
           Object.assign(config, configBackend)&lt;br /&gt;
&lt;br /&gt;
           app&lt;br /&gt;
             .provide(&#039;config&#039;, config)&lt;br /&gt;
             .use(pinia)&lt;br /&gt;
             .use(router)&lt;br /&gt;
             .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
         }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt es im Konfigurationsobjekt keine Defaultsprache mehr.&lt;br /&gt;
Im I18N-Store wird daher ab sofort die erste Sprache in der Liste der&lt;br /&gt;
unterstützten Sprachen als Defaultsprache genutzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/storeI18N.js&lt;br /&gt;
&lt;br /&gt;
initialize = &lt;br /&gt;
  async resolve =&amp;gt; &lt;br /&gt;
        { await changeLang(config.languages[0]); &lt;br /&gt;
          resolve(); &lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06d ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Defaultmäßig soll dem Benutzer die App in der Sprache angezeigt werden, die der Benutzer&lt;br /&gt;
im Browser voreingestellt hat. Sollte die voreingestellte Sprache nicht unterstützt werden, wird die App in der Defaultsprache angezeigt.&lt;br /&gt;
&lt;br /&gt;
Legen Sie zunächst wieder einen neuen Branch an.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v06c     # Wechsle in den Branch v06c&lt;br /&gt;
git checkout -b v06d  # Klone v06c in einen neuen Branch v06d&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Vorgehen ist im Prinzip ganz einfach.&lt;br /&gt;
&lt;br /&gt;
Mittels des NPM-Packets &amp;quot;navigator-languages&amp;quot; ermittelt man die vom Browser unterstützten&lt;br /&gt;
Sprachen.  In der Konfigurationsdatei listet man die Sprachen auf, die von der App unterstützt werden.&lt;br /&gt;
Besser wäre es natürlich, diese Liste vom Backend zu laden, damit die App mit bekommt, wenn neue&lt;br /&gt;
Sprachen zur Verfügung stehen. Am aufwändigsten ist es, diese beiden Array zu matchen.&lt;br /&gt;
&lt;br /&gt;
Mögliche Erweiterung: Auch die HTML-Pfade sollten internationalisiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm i navigator-languages // unterstützt mehrere Browservarianten&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Language Matching ===&lt;br /&gt;
&lt;br /&gt;
Das Language Matching erfolgt zweimal mit zwei verschachtelten Schleifen.&lt;br /&gt;
Zunächst wird überprüft, ob es exakte Übereinstimmungen gibt. Sollte das nicht der Fall sein, wird überprüft, ob es Matches zwischen den ersten beiden Buchstaben gibt (&#039;de&#039;, &#039;de-AT&#039;, &#039;de-DE&#039; matchen in diesem Schritt alle miteinander).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/util/chooseLanguage.js&lt;br /&gt;
&lt;br /&gt;
import navigatorLanguages from &#039;navigator-languages&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  /**&lt;br /&gt;
   * @function&lt;br /&gt;
   * @param  { Array&amp;lt;string&amp;gt; } p_langs_available &lt;br /&gt;
   *         An array of language strings containing the languages&lt;br /&gt;
   *         supported by the app &lt;br /&gt;
   * @param  { Array&amp;lt;string&amp;gt; } [p_langs_required  = navigatorLanguages()]&lt;br /&gt;
   *         An array of language strings containing the languages&lt;br /&gt;
   *         requested by the client &lt;br /&gt;
   * @return { string } &lt;br /&gt;
   *         The language to be displayed&lt;br /&gt;
   */&lt;br /&gt;
  chooseLanguage =&lt;br /&gt;
  (p_langs_available, p_langs_required = navigatorLanguages()) =&amp;gt;&lt;br /&gt;
  { let&lt;br /&gt;
      lang = p_langs_available[0],&lt;br /&gt;
      i=0, j=0, n=p_langs_required.length, m=p_langs_available.length&lt;br /&gt;
  &lt;br /&gt;
    outer:&lt;br /&gt;
    { while (i&amp;lt;n)&lt;br /&gt;
      { const c_lang_required = p_langs_required[i]&lt;br /&gt;
        while (j&amp;lt;m)&lt;br /&gt;
        { const c_lang_availabe = p_langs_available[j]&lt;br /&gt;
          if (c_lang_availabe === c_lang_required)&lt;br /&gt;
          // both languages match perfectly&lt;br /&gt;
          { lang = c_lang_availabe;&lt;br /&gt;
            break outer;&lt;br /&gt;
          }&lt;br /&gt;
          j++&lt;br /&gt;
        }&lt;br /&gt;
        i++&lt;br /&gt;
      }&lt;br /&gt;
      i=0, j=0&lt;br /&gt;
      while (i&amp;lt;n)&lt;br /&gt;
      { const c_lang_required = p_langs_required[i]&lt;br /&gt;
        while (j&amp;lt;m)&lt;br /&gt;
        { const c_lang_availabe = p_langs_available[j]&lt;br /&gt;
          if (c_lang_availabe.substring(0,2) === c_lang_required.substring(0,2))&lt;br /&gt;
          // main part of the languages match&lt;br /&gt;
          { lang = c_lang_availabe;&lt;br /&gt;
            break outer;&lt;br /&gt;
          }&lt;br /&gt;
          j++&lt;br /&gt;
        }&lt;br /&gt;
        i++&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  &lt;br /&gt;
     return lang&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
export default chooseLanguage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/store/StoreI18n&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Im I18N-Store wird nun beim Initialisieren nicht nicht mehr eine Defaultspache geladen, sondern &lt;br /&gt;
die Sprache, die möglichst nah an den Wünschen des im Browser des Benutzers gespeicherten Sprachwünschen liegt.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass Sie in die Datei &amp;lt;code&amp;gt;vite.config.common.js&amp;lt;/code&amp;gt; einen geeigneten Alias für den &lt;br /&gt;
&amp;lt;code&amp;gt;util&amp;lt;/code&amp;gt;-Pfad definieren müssen, um &amp;lt;code&amp;gt;&#039;/util/chooseLanguage&#039;&amp;lt;/code&amp;gt; an Stelle von&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/util/chooseLanguage&#039;&amp;lt;/code&amp;gt; schreiben zu können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
import chooseLanguage from &#039;/util/chooseLanguage&#039;&lt;br /&gt;
...&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          ...&lt;br /&gt;
          initialize = &lt;br /&gt;
             async resolve =&amp;gt; &lt;br /&gt;
                   { await changeLang(chooseLanguage(config.languages)); &lt;br /&gt;
                     resolve(); &lt;br /&gt;
                   }&lt;br /&gt;
        ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 07|Teil 7 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_06&amp;diff=53252</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 06</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_06&amp;diff=53252"/>
		<updated>2025-05-23T09:40:49Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Hello World Vue 06b */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v06&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v06a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v06b&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
Die Version des [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05|fünften Teils des Vue-Tutoriums]]&lt;br /&gt;
wird so angepasst, dass die Sprache zur Laufzeit geändert werden kann. &lt;br /&gt;
&lt;br /&gt;
Zunächst wird jedoch eine Navigationskomponente eingeführt. Dies hat den Zweck, das Routing an einem Beispiel zu erklären.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05d    # Wechsle in den Branch v05d mit Express-Server&lt;br /&gt;
git checkout -b v06  # Klone v05d in einen neuen Branch v06&lt;br /&gt;
rm -rf express_Hello_world&lt;br /&gt;
cd frontend&lt;br /&gt;
npm i&lt;br /&gt;
npm i vue-router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;public/json/config.jso&amp;lt;/code&amp;gt;&lt;br /&gt;
aktivieren wir wieder den Zugriff auf die lokalen JSON-Dateien.&lt;br /&gt;
Den Zugriff auf den Backend-Server, um die Bilder von dort zu holen, behandeln wir später.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fügen Sie außerdem in die Datei &amp;lt;code&amp;gt;vite.config.common.js&amp;lt;/code&amp;gt; zwei geeignete Aliase für &lt;br /&gt;
&amp;lt;code&amp;gt;view&amp;lt;/code&amp;gt; und &amp;lt;/code&amp;gt;router&amp;lt;/code&amp;gt; ein.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Anstelle von Sections werden im Frontend Views verwendet.&lt;br /&gt;
Dazu werden die Section-Komponenten in den Ordner View verschoben und umbenannt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
src/view/ViewHello.vue&lt;br /&gt;
src/view/ViewHome.vue     // an Stelle von ViewForm.vue&lt;br /&gt;
&lt;br /&gt;
src/view/ViewError404.vue // neu&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die CSS-Dateien müssen natürlich auch entsprechend verschoben und abgepasst werden.&lt;br /&gt;
&lt;br /&gt;
ViewForm.vue wird in ViewHome.vue unbenannt,&lt;br /&gt;
da diese Seite als Startseite verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird eine Seite ViewError404.vue angelegt, die immer dann ausgeliefert wird, wenn der Benutzer auf eine URL zugreift, der keine View-Datei zugeordnet ist.&lt;br /&gt;
&lt;br /&gt;
Die CSS-Dateien sollten analog umbenannt und unter den neuen Namen importiert werden. Für &amp;lt;code&amp;gt;ViewError404.vue&amp;lt;/code&amp;gt; muss natürlich auch eine CSS-Datei angelegt werden. Diese verweist allerdings nur auf &amp;lt;code&amp;gt;View.css&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/view/ViewError404.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import router    from &#039;/router&#039;&lt;br /&gt;
  import storeI18n from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const phrases = storeI18n().phrases&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;h1&amp;gt;404&amp;lt;/h1&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;{{phrases.pageNotFound.replace(&#039;$1&#039;, router.currentRoute.value.path)}}&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/view/ViewError404.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Router-Plugin ===&lt;br /&gt;
&lt;br /&gt;
Um mit Vue Multipage-Anwendungen realisieren zu können, benötigt man einRouter-Plugin.&lt;br /&gt;
Dieses muss  zunächst händisch auf Basis von &amp;lt;code&amp;gt;vue-router&amp;lt;/code&amp;gt; implementiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/router/index.js&lt;br /&gt;
&lt;br /&gt;
import { createRouter, createWebHistory } from &#039;vue-router&#039;&lt;br /&gt;
import ViewHome                           from &#039;/view/ViewHome.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  routes =&lt;br /&gt;
    [ { path: &#039;/&#039;,&lt;br /&gt;
        name: &#039;home&#039;,&lt;br /&gt;
        component: ViewHome,&lt;br /&gt;
      },&lt;br /&gt;
      { path: &#039;/hello&#039;,&lt;br /&gt;
        name: &#039;hello&#039;,&lt;br /&gt;
        component: () =&amp;gt; import(&#039;/view/ViewHello.vue&#039;),&lt;br /&gt;
      },&lt;br /&gt;
      { path:      &#039;/:pathMatch(.*)&#039;,&lt;br /&gt;
        component: () =&amp;gt; import(&#039;/view/ViewError404.vue&#039;),&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
&lt;br /&gt;
  router =&lt;br /&gt;
    createRouter({ history: createWebHistory(), routes })&lt;br /&gt;
&lt;br /&gt;
export default router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun muss man das neu erstellte Plugin in die App integrieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import router          from &#039;./router&#039;  // &#039;./router/index.js&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
const&lt;br /&gt;
  pinia   = createPinia(),&lt;br /&gt;
  app     = createApp(App),&lt;br /&gt;
  init    = async () =&amp;gt;&lt;br /&gt;
            { app&lt;br /&gt;
                .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
                .use(pinia)&lt;br /&gt;
                .use(router)&lt;br /&gt;
                .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== index.html ===&lt;br /&gt;
&lt;br /&gt;
Da das &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;-Tag nun innerhalb von &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; verwendet werden soll, &lt;br /&gt;
wird es aus der Datei &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; entfernt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  &amp;lt;body id=&amp;quot;app&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== App.vue ===&lt;br /&gt;
&lt;br /&gt;
Nun kann in die App eine Navigation eingebaut werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- /src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import AppNav       from &#039;/component/AppNav.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeI18n    from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  storeI18n();&lt;br /&gt;
&lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p      v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;AppNav v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;main   v-if=&amp;quot;session.isInitialized&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;router-view/&amp;gt;&lt;br /&gt;
  &amp;lt;/main&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
  @import &#039;/css/body.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zugehörige Navigationskomponente muss auch noch implementiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- /src/component/AppNav.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;nav&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/&amp;quot;&amp;gt;Home&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/hello&amp;quot;&amp;gt;Hello&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
  &amp;lt;/nav&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/AppNav.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* src/css/_config.css */&lt;br /&gt;
...&lt;br /&gt;
--background-color:  #C5EFFC;&lt;br /&gt;
--link-color:        #0027d4;&lt;br /&gt;
...&lt;br /&gt;
--nav-dist:          0.5em;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* src/css/AppNav.css */&lt;br /&gt;
&lt;br /&gt;
@import &#039;config.css&#039;;&lt;br /&gt;
&lt;br /&gt;
nav&lt;br /&gt;
{ top:      0;&lt;br /&gt;
  width:    100%;&lt;br /&gt;
  position: fixed;&lt;br /&gt;
&lt;br /&gt;
  ul&lt;br /&gt;
  { list-style-type: none;&lt;br /&gt;
    margin: 0 var(--nav-dist);&lt;br /&gt;
    padding: 0;&lt;br /&gt;
&lt;br /&gt;
    li&lt;br /&gt;
    { display: inline;&lt;br /&gt;
      a { padding: 0 var(--nav-dist); }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    a&lt;br /&gt;
    { text-decoration: none;&lt;br /&gt;
      color:           var(--link-color);&lt;br /&gt;
      font-weight:     bold;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    a:hover, .router-link-exact-active&lt;br /&gt;
    { text-decoration: underline;&lt;br /&gt;
      color:           var(--link-color);&lt;br /&gt;
      font-weight:     bold;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  label, input&lt;br /&gt;
  { margin: 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Navigation ===&lt;br /&gt;
Die Navigation muss in allen Komponenten, in den Seiten gewechselt werden, angepasst werden.&lt;br /&gt;
&lt;br /&gt;
Das ist in der App.vue der Fall (siehe zuvor) sowie in der ViewHome.vue.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- /src/vie/ViewHome.vue  --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
  import router        from &#039;/router&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    sayHello   = () =&amp;gt; router.push(&#039;/hello&#039;)&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Section-Verwaltung der ehemaligen Single-Page-Anwendung kann und sollte gelöscht werden:&lt;br /&gt;
* &amp;lt;code&amp;gt;config.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;storeSession.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ViewHome.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ViewHello.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; kann (samt CSS-Datei) ganz gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06a ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die Ausgabesprache soll vom Benutzer zur Laufzeit geändert werden können. Die Sprachen sollen durch Bilder (Flaggen) symbolisiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v06      # Wechsle in den Branch v06&lt;br /&gt;
git checkout -b v06a  # Klone v06 in einen neuen Branch v06a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Um die vorhandenen Sprachen durchblättern zu können, wird ein einfacher Automat verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
  &amp;quot;nextLanguage&amp;quot;:    {&amp;quot;de&amp;quot;: &amp;quot;en&amp;quot;, &amp;quot;en&amp;quot;: &amp;quot;de&amp;quot;}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== StoreI18n.js: Integration von nextLanguage ===&lt;br /&gt;
&lt;br /&gt;
Der Automat &amp;lt;code&amp;gt;nextLanguage&amp;lt;/code&amp;gt; wird in den StoreI18n integriert. Wenn die Methode &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; ohne Argument aufgerufen wird, soll der Automat die aktuelle Sprache &amp;lt;code&amp;gt;lang&amp;lt;/code&amp;gt; durch die Nachfolgersprache ersetzen.&lt;br /&gt;
Dazu sollte zunächst die zunächst Konstante &amp;lt;code&amp;gt;nextLanguage&amp;lt;/code&amp;gt; definiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
lang =&lt;br /&gt;
  ref(null),&lt;br /&gt;
&lt;br /&gt;
nextLanguage =&lt;br /&gt;
  reactive(config.nextLanguage),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese neue Konstante &amp;lt;code&amp;gt;nextLanguage&amp;lt;/code&amp;gt; wird verwendet, um die Sprache mittels &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; zu definieren, sofern&lt;br /&gt;
kein Argument übergeben wurde.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; soll bei einer Änderung der Sprache sowohl die notwendige JSON-Sprach-Datei laden als auch die in der HTML-Datei &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; ausgewiesene Sprache anpassen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/storeI18N.js&lt;br /&gt;
&lt;br /&gt;
changeLang =&lt;br /&gt;
  async (p_lang = null) =&amp;gt;&lt;br /&gt;
        { lang.value = p_lang ?? nextLanguage[lang.value];&lt;br /&gt;
          await getI18n(lang.value);&lt;br /&gt;
          document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
initialize = &lt;br /&gt;
  async resolve =&amp;gt; &lt;br /&gt;
        { await changeLang(config.defaultLanguage); &lt;br /&gt;
          resolve(); &lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bilder von Flaggen ===&lt;br /&gt;
&lt;br /&gt;
Zur Visualisierung der gewählten Sprache werden Bilder von Flaggen eingesetzt.&lt;br /&gt;
Wir kopieren die Bilder im SVG-Format aus dem NPM-Paket &amp;quot;https://github.com/lipis/flag-icons&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D flag-icons&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Ordner &amp;quot;node_modules/flag-icons/flags/4x3&amp;quot; finden Sie Flaggen zahlreicher Länder im 4-zu-3-Format.&lt;br /&gt;
Kopieren Sie die Flaggen der Länder Deutschland und Großbritanien in den Ordner &amp;lt;code&amp;gt;public/img&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In die Datei config.js wird der Pfad, unter dem die Bilder zu finden sind, eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
//   public/config.js&lt;br /&gt;
...&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:  &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;imgPath&amp;quot;:  &amp;quot;/img/$1.svg&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;: &amp;quot;/api/$1&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;FormButtonIcon&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Komponente wird benötigt: FormButtonIcon&lt;br /&gt;
Dabei handelt es sich um einen Button, der ein Icon anzeigt.&lt;br /&gt;
Das Bild kann zur Laufzeit geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import { computed, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    props       = defineProps&lt;br /&gt;
                  ({ image: { type: String },&lt;br /&gt;
                     width: { type: String, default: &#039;3em&#039;},&lt;br /&gt;
                  }),&lt;br /&gt;
    config      = inject(&#039;config&#039;),&lt;br /&gt;
    emit        = defineEmits([&#039;click&#039;]),&lt;br /&gt;
    click       = () =&amp;gt; emit(&#039;click&#039;),&lt;br /&gt;
    require     = v_url =&amp;gt; import.meta.url == null&lt;br /&gt;
                           ? v_url&lt;br /&gt;
                           : new URL(v_url, import.meta.url).href,&lt;br /&gt;
    imgPath     = config.imgPath,&lt;br /&gt;
    buttonImage = computed(() =&amp;gt; require(imgPath.replace(&#039;$1&#039;, props.image)))&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;input type=&amp;quot;image&amp;quot; :src=&amp;quot;buttonImage&amp;quot; :style=&amp;quot;{width}&amp;quot; @click=&amp;quot;click&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/form/FormButtonIcon.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
/* src/css/component/FormButtonIcon.css */&lt;br /&gt;
&lt;br /&gt;
@import &#039;FormButton.css&#039;;&lt;br /&gt;
&lt;br /&gt;
input&lt;br /&gt;
{ width:       $button_icon_width !important;&lt;br /&gt;
  margin-left: $nav-dist          !important;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fügen Sie in &amp;lt;code&amp;gt;src/css/_config.css&amp;lt;/code&amp;gt; noch &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
--button-icon-width: 1em;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ein.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterung von &amp;lt;code&amp;gt;AppNav.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Sprachwahl-Button muss noch in die Navigation eingebaut werden. In diesem Rahmen wird auch gleich die Navigation etwas internationalisiert.&lt;br /&gt;
&lt;br /&gt;
Ergänzen Sie in den Dictionaries der Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt;&lt;br /&gt;
folgende Attribute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
    ...         ...,&lt;br /&gt;
    &amp;quot;navHome&amp;quot;:  &amp;quot;Start&amp;quot;,&lt;br /&gt;
    &amp;quot;navHello&amp;quot;: &amp;quot;Hallo&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
    ...         ...,&lt;br /&gt;
    &amp;quot;navHome&amp;quot;:  &amp;quot;Home&amp;quot;,&lt;br /&gt;
    &amp;quot;navHello&amp;quot;: &amp;quot;Hello&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können Sie das Navigationsmenü internationalisieren (einschließlich Auswahl der Sprache per Button).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/component/AppNav.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButtonIcon from &#039;/component/form/FormButtonIcon.vue&#039;&lt;br /&gt;
  import storeI18n      from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const i18n = storeI18n()&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;nav&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/&amp;quot;&amp;gt;{{i18n.dictionary.navHome}}&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/hello&amp;quot;&amp;gt;{{i18n.dictionary.navHello}}&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;FormButtonIcon :image=&amp;quot;i18n.lang&amp;quot; @click=&amp;quot;i18n.changeLang()&amp;quot;/&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
  &amp;lt;/nav&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/AppNav.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06b ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Sorgen Sie dafür, dass &amp;lt;code&amp;gt;autofocus&amp;lt;/code&amp;gt; korrekt funktioniert, insbesondere beim Seitenwechsel via Navigation.&lt;br /&gt;
&lt;br /&gt;
Es ist sinnvoll, die Funktionalität in ein eigenes Store-Modul auszulagern, damit&lt;br /&gt;
das Session-Modul in Anwendungen, die keine Autofocus-Funktionalität benötigen, keinen&lt;br /&gt;
überflüssigen Code enthalten (Bertrand Meyer: Jedes Modul erfüllt genau eine Aufgabe).&lt;br /&gt;
Das Session-Modul wird entsprechend abgespeckt.&lt;br /&gt;
 &lt;br /&gt;
Der Autofocus-Store soll zwei Funktionen bieten:&lt;br /&gt;
* &amp;lt;code&amp;gt;setAutofocusId&amp;lt;/code&amp;gt;: Speichert die ID des Autofokus-Elements der aktuellen Seite (Default: &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;lt;code&amp;gt;setFocus&amp;lt;/code&amp;gt;: Setzt den Focus auf die aktuelle Autofokus-Komponente (sofern die ID ungleich &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; ist). Insbesondere für das Window-Ereignis &amp;lt;code&amp;gt;&#039;visibilitychange&#039;&amp;lt;/code&amp;gt; soll diese Funktion&lt;br /&gt;
aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/StoreAutofocus.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore } from &#039;pinia&#039;&lt;br /&gt;
import { ref }         from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeAutofocus =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;autofocus&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      c_autofocus_id = ref(),&lt;br /&gt;
      setFocus       = () =&amp;gt; document.getElementById(c_autofocus_id.value)?.focus(),&lt;br /&gt;
      setAutofocusId = p_autofocus_id =&amp;gt; { c_autofocus_id.value = p_autofocus_id; &lt;br /&gt;
                                           setFocus();&lt;br /&gt;
                                         }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;visibilitychange&#039;, setFocus);&lt;br /&gt;
&lt;br /&gt;
    return { setAutofocusId, setFocus }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeAutofocus&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore } from &#039;pinia&#039;&lt;br /&gt;
import { ref }         from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die aktuelle ID speichern zu können, muss das zugehörige Textfeld die Information, welche ID es hat, &lt;br /&gt;
per &amp;lt;code&amp;gt;emit&amp;lt;/code&amp;gt; an die Elternkomponente ausgeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
// /src/component/form/FormTextfield.vue&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
const&lt;br /&gt;
  props = defineProps&lt;br /&gt;
          ({ id: { type: String,  default: &#039;&#039; },&lt;br /&gt;
             ...&lt;br /&gt;
          })&lt;br /&gt;
  ... &lt;br /&gt;
  emit  = defineEmits([&#039;id&#039;, &#039;update:text&#039;, &#039;enter&#039;]),&lt;br /&gt;
...&lt;br /&gt;
emit(&#039;id&#039;, inputId);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen die Views, die ein Autofokus-Feld beinhalten, dieses an den Session-Store melden, &lt;br /&gt;
sobald sie per Routing aktiviert werden. Das ist in unserem Fall nur die &amp;lt;code&amp;gt;ViewHome&amp;lt;/code&amp;gt;.&lt;br /&gt;
Ein Watcher sorgt dafür, dass die ID des Textfeldes im Session-Objekt gespeichert wird, sobald &lt;br /&gt;
das zugehörige Textfeld die ID per &amp;lt;code&amp;gt;emit&amp;lt;/code&amp;gt; meldet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
// /src/view/ViewHome.vue&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import { ref, watch } from &#039;vue&#039;&lt;br /&gt;
  ...&lt;br /&gt;
  import storeAutofocus from &#039;/store/StoreAutofocus&#039;&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    id         = ref(null),&lt;br /&gt;
    autofocus  = storeAutofocus(),&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
  watch( id, () =&amp;gt; autofocus.setAutofocusId(id.value) )&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
        &amp;lt;FormTextfield @id=&amp;quot;setId&amp;quot;&lt;br /&gt;
          ...&lt;br /&gt;
        &amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schluss muss im Router noch die Autofocus-ID bei jedem Seitenwechsel gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/router/index.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import storeAutofocus from &#039;/store/StoreAutofocus&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
router.beforeEach(() =&amp;gt; storeAutofocus().setAutofocusId())&lt;br /&gt;
&lt;br /&gt;
export default router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist hier, dass man keine Konstante &amp;lt;code&amp;gt;session&amp;lt;/code&amp;gt; definieren kann, da die Stores erst nach dem Router-Objekt erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/router/index.js&lt;br /&gt;
...&lt;br /&gt;
import storeAutofocus from &#039;/store/StoreAutofocus&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  autofocus = storeAutofocus(), // FUNKTIONIERT NICHT&lt;br /&gt;
  ...&lt;br /&gt;
router.beforeEach(() =&amp;gt; autofocus.setAutofocusId())&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Funktionsaufruf &amp;lt;code&amp;gt;storeSession()&amp;lt;/code&amp;gt; darf daher erst in  der Callback-Funktion von &lt;br /&gt;
&amp;lt;code&amp;gt;router.beforeEach&amp;lt;/code&amp;gt; aufgerufen werden. Zu diesem Zeitpunkt sind die Stores und der Router auf jeden Fall schon initialisiert worden.&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss sollten Sie noch versuchen, &amp;lt;code&amp;gt;/src/component/AppNav.vue&amp;lt;/code&amp;gt; so abzuändern,&lt;br /&gt;
dass ein Klick auf die Fahne nicht nur die Sprache ändert, sondern auch noch den Focus auf das Startfeld setzt.&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06c ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Sorgen Sie dafür, dass die unterstützten Sprachen und der Sprach-Automat vom Backend&lt;br /&gt;
geladen wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v06b     # Wechsle in den Branch v06b&lt;br /&gt;
git checkout -b v06c  # Klone v06b in einen neuen Branch v06c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Splitten Sie die Config-Datei zunächst in zwei Teile: &amp;quot;config.json&amp;quot; wird aus dem Public-Ordner geladen.&lt;br /&gt;
&amp;quot;config_backend.json&amp;quot; wird normalerweise vom Backend geladen, kann aber zum Entwicklungszeitpunkt auch vom Public-Ordner geladen werden, wenn das Backend noch nicht stabil läuft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;configBackend&amp;quot;:  &amp;quot;/json/config_backend.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;imgPath&amp;quot;:        &amp;quot;/img/$1.svg&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:       &amp;quot;/api/$1&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/public/json/config_backend.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;languages&amp;quot;:    [&amp;quot;de&amp;quot;, &amp;quot;en&amp;quot;],&lt;br /&gt;
  &amp;quot;nextLanguage&amp;quot;: {&amp;quot;de&amp;quot;: &amp;quot;en&amp;quot;, &amp;quot;en&amp;quot;: &amp;quot;de&amp;quot;}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Backend teilt dem Frontend mit, welche Sprachen unterstützt werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Laden Sie, nachdem Sie &amp;quot;config&amp;quot; asynchron geladen haben, &amp;quot;configBackend&amp;quot; asynchron und kopieren Sie den Inhalt dieses Objekts in das Config-Objekt. Den Pfad von &amp;quot;configBackend&amp;quot; finden Sie unter &amp;quot;config.configBackend&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
const&lt;br /&gt;
  ...&lt;br /&gt;
  init = async () =&amp;gt;&lt;br /&gt;
         { const&lt;br /&gt;
             config        = await getJson(&#039;/json/config.json&#039;),&lt;br /&gt;
             configBackend = await getJson(config.configBackend)&lt;br /&gt;
&lt;br /&gt;
           Object.assign(config, configBackend)&lt;br /&gt;
&lt;br /&gt;
           app&lt;br /&gt;
             .provide(&#039;config&#039;, config)&lt;br /&gt;
             .use(pinia)&lt;br /&gt;
             .use(router)&lt;br /&gt;
             .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
         }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt es im Konfigurationsobjekt keine Defaultsprache mehr.&lt;br /&gt;
Im I18N-Store wird daher ab sofort die erste Sprache in der Liste der&lt;br /&gt;
unterstützten Sprachen als Defaultsprache genutzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/storeI18N.js&lt;br /&gt;
&lt;br /&gt;
initialize = &lt;br /&gt;
  async resolve =&amp;gt; &lt;br /&gt;
        { await changeLang(config.languages[0]); &lt;br /&gt;
          resolve(); &lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06d ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Defaultmäßig soll dem Benutzer die App in der Sprache angezeigt werden, die der Benutzer&lt;br /&gt;
im Browser voreingestellt hat. Sollte die voreingestellte Sprache nicht unterstützt werden, wird die App in der Defaultsprache angezeigt.&lt;br /&gt;
&lt;br /&gt;
Legen Sie zunächst wieder einen neuen Branch an.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v06c     # Wechsle in den Branch v06c&lt;br /&gt;
git checkout -b v06d  # Klone v06c in einen neuen Branch v06d&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Vorgehen ist im Prinzip ganz einfach.&lt;br /&gt;
&lt;br /&gt;
Mittels des NPM-Packets &amp;quot;navigator-languages&amp;quot; ermittelt man die vom Browser unterstützten&lt;br /&gt;
Sprachen.  In der Konfigurationsdatei listet man die Sprachen auf, die von der App unterstützt werden.&lt;br /&gt;
Besser wäre es natürlich, diese Liste vom Backend zu laden, damit die App mit bekommt, wenn neue&lt;br /&gt;
Sprachen zur Verfügung stehen. Am aufwändigsten ist es, diese beiden Array zu matchen.&lt;br /&gt;
&lt;br /&gt;
Mögliche Erweiterung: Auch die HTML-Pfade sollten internationalisiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm i navigator-languages // unterstützt mehrere Browservarianten&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Language Matching ===&lt;br /&gt;
&lt;br /&gt;
Das Language Matching erfolgt zweimal mit zwei verschachtelten Schleifen.&lt;br /&gt;
Zunächst wird überprüft, ob es exakte Übereinstimmungen gibt. Sollte das nicht der Fall sein, wird überprüft, ob es Matches zwischen den ersten beiden Buchstaben gibt (&#039;de&#039;, &#039;de-AT&#039;, &#039;de-DE&#039; matchen in diesem Schritt alle miteinander).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/util/chooseLanguage.js&lt;br /&gt;
&lt;br /&gt;
import navigatorLanguages from &#039;navigator-languages&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  /**&lt;br /&gt;
   * @function&lt;br /&gt;
   * @param  { Array&amp;lt;string&amp;gt; } p_langs_available &lt;br /&gt;
   *         An array of language strings containing the languages&lt;br /&gt;
   *         supported by the app &lt;br /&gt;
   * @param  { Array&amp;lt;string&amp;gt; } [p_langs_required  = navigatorLanguages()]&lt;br /&gt;
   *         An array of language strings containing the languages&lt;br /&gt;
   *         requested by the client &lt;br /&gt;
   * @return { string } &lt;br /&gt;
   *         The language to be displayed&lt;br /&gt;
   */&lt;br /&gt;
  chooseLanguage =&lt;br /&gt;
  (p_langs_available, p_langs_required = navigatorLanguages()) =&amp;gt;&lt;br /&gt;
  { let&lt;br /&gt;
      lang = p_langs_available[0],&lt;br /&gt;
      i=0, j=0, n=p_langs_required.length, m=p_langs_available.length&lt;br /&gt;
  &lt;br /&gt;
    outer:&lt;br /&gt;
    { while (i&amp;lt;n)&lt;br /&gt;
      { const c_lang_required = p_langs_required[i]&lt;br /&gt;
        while (j&amp;lt;m)&lt;br /&gt;
        { const c_lang_availabe = p_langs_available[j]&lt;br /&gt;
          if (c_lang_availabe === c_lang_required)&lt;br /&gt;
          // both languages match perfectly&lt;br /&gt;
          { lang = c_lang_availabe;&lt;br /&gt;
            break outer;&lt;br /&gt;
          }&lt;br /&gt;
          j++&lt;br /&gt;
        }&lt;br /&gt;
        i++&lt;br /&gt;
      }&lt;br /&gt;
      i=0, j=0&lt;br /&gt;
      while (i&amp;lt;n)&lt;br /&gt;
      { const c_lang_required = p_langs_required[i]&lt;br /&gt;
        while (j&amp;lt;m)&lt;br /&gt;
        { const c_lang_availabe = p_langs_available[j]&lt;br /&gt;
          if (c_lang_availabe.substring(0,2) === c_lang_required.substring(0,2))&lt;br /&gt;
          // main part of the languages match&lt;br /&gt;
          { lang = c_lang_availabe;&lt;br /&gt;
            break outer;&lt;br /&gt;
          }&lt;br /&gt;
          j++&lt;br /&gt;
        }&lt;br /&gt;
        i++&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  &lt;br /&gt;
     return lang&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
export default chooseLanguage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/store/StoreI18n&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Im I18N-Store wird nun beim Initialisieren nicht nicht mehr eine Defaultspache geladen, sondern &lt;br /&gt;
die Sprache, die möglichst nah an den Wünschen des im Browser des Benutzers gespeicherten Sprachwünschen liegt.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass Sie in die Datei &amp;lt;code&amp;gt;vite.config.common.js&amp;lt;/code&amp;gt; einen geeigneten Alias für den &lt;br /&gt;
&amp;lt;code&amp;gt;util&amp;lt;/code&amp;gt;-Pfad definieren müssen, um &amp;lt;code&amp;gt;&#039;/util/chooseLanguage&#039;&amp;lt;/code&amp;gt; an Stelle von&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/util/chooseLanguage&#039;&amp;lt;/code&amp;gt; schreiben zu können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
import chooseLanguage from &#039;/util/chooseLanguage&#039;&lt;br /&gt;
...&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          ...&lt;br /&gt;
          initialize = &lt;br /&gt;
             async resolve =&amp;gt; &lt;br /&gt;
                   { await changeLang(chooseLanguage(config.languages)); &lt;br /&gt;
                     resolve(); &lt;br /&gt;
                   }&lt;br /&gt;
        ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 07|Teil 7 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_06&amp;diff=53251</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 06</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_06&amp;diff=53251"/>
		<updated>2025-05-20T07:10:11Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Hello World Vue 06b */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v06&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v06a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v06b&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
Die Version des [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05|fünften Teils des Vue-Tutoriums]]&lt;br /&gt;
wird so angepasst, dass die Sprache zur Laufzeit geändert werden kann. &lt;br /&gt;
&lt;br /&gt;
Zunächst wird jedoch eine Navigationskomponente eingeführt. Dies hat den Zweck, das Routing an einem Beispiel zu erklären.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05d    # Wechsle in den Branch v05d mit Express-Server&lt;br /&gt;
git checkout -b v06  # Klone v05d in einen neuen Branch v06&lt;br /&gt;
rm -rf express_Hello_world&lt;br /&gt;
cd frontend&lt;br /&gt;
npm i&lt;br /&gt;
npm i vue-router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;public/json/config.jso&amp;lt;/code&amp;gt;&lt;br /&gt;
aktivieren wir wieder den Zugriff auf die lokalen JSON-Dateien.&lt;br /&gt;
Den Zugriff auf den Backend-Server, um die Bilder von dort zu holen, behandeln wir später.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fügen Sie außerdem in die Datei &amp;lt;code&amp;gt;vite.config.common.js&amp;lt;/code&amp;gt; zwei geeignete Aliase für &lt;br /&gt;
&amp;lt;code&amp;gt;view&amp;lt;/code&amp;gt; und &amp;lt;/code&amp;gt;router&amp;lt;/code&amp;gt; ein.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Anstelle von Sections werden im Frontend Views verwendet.&lt;br /&gt;
Dazu werden die Section-Komponenten in den Ordner View verschoben und umbenannt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
src/view/ViewHello.vue&lt;br /&gt;
src/view/ViewHome.vue     // an Stelle von ViewForm.vue&lt;br /&gt;
&lt;br /&gt;
src/view/ViewError404.vue // neu&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die CSS-Dateien müssen natürlich auch entsprechend verschoben und abgepasst werden.&lt;br /&gt;
&lt;br /&gt;
ViewForm.vue wird in ViewHome.vue unbenannt,&lt;br /&gt;
da diese Seite als Startseite verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird eine Seite ViewError404.vue angelegt, die immer dann ausgeliefert wird, wenn der Benutzer auf eine URL zugreift, der keine View-Datei zugeordnet ist.&lt;br /&gt;
&lt;br /&gt;
Die CSS-Dateien sollten analog umbenannt und unter den neuen Namen importiert werden. Für &amp;lt;code&amp;gt;ViewError404.vue&amp;lt;/code&amp;gt; muss natürlich auch eine CSS-Datei angelegt werden. Diese verweist allerdings nur auf &amp;lt;code&amp;gt;View.css&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/view/ViewError404.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import router    from &#039;/router&#039;&lt;br /&gt;
  import storeI18n from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const phrases = storeI18n().phrases&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;h1&amp;gt;404&amp;lt;/h1&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;{{phrases.pageNotFound.replace(&#039;$1&#039;, router.currentRoute.value.path)}}&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/view/ViewError404.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Router-Plugin ===&lt;br /&gt;
&lt;br /&gt;
Um mit Vue Multipage-Anwendungen realisieren zu können, benötigt man einRouter-Plugin.&lt;br /&gt;
Dieses muss  zunächst händisch auf Basis von &amp;lt;code&amp;gt;vue-router&amp;lt;/code&amp;gt; implementiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/router/index.js&lt;br /&gt;
&lt;br /&gt;
import { createRouter, createWebHistory } from &#039;vue-router&#039;&lt;br /&gt;
import ViewHome                           from &#039;/view/ViewHome.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  routes =&lt;br /&gt;
    [ { path: &#039;/&#039;,&lt;br /&gt;
        name: &#039;home&#039;,&lt;br /&gt;
        component: ViewHome,&lt;br /&gt;
      },&lt;br /&gt;
      { path: &#039;/hello&#039;,&lt;br /&gt;
        name: &#039;hello&#039;,&lt;br /&gt;
        component: () =&amp;gt; import(&#039;/view/ViewHello.vue&#039;),&lt;br /&gt;
      },&lt;br /&gt;
      { path:      &#039;/:pathMatch(.*)&#039;,&lt;br /&gt;
        component: () =&amp;gt; import(&#039;/view/ViewError404.vue&#039;),&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
&lt;br /&gt;
  router =&lt;br /&gt;
    createRouter({ history: createWebHistory(), routes })&lt;br /&gt;
&lt;br /&gt;
export default router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun muss man das neu erstellte Plugin in die App integrieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import router          from &#039;./router&#039;  // &#039;./router/index.js&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
const&lt;br /&gt;
  pinia   = createPinia(),&lt;br /&gt;
  app     = createApp(App),&lt;br /&gt;
  init    = async () =&amp;gt;&lt;br /&gt;
            { app&lt;br /&gt;
                .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
                .use(pinia)&lt;br /&gt;
                .use(router)&lt;br /&gt;
                .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== index.html ===&lt;br /&gt;
&lt;br /&gt;
Da das &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;-Tag nun innerhalb von &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; verwendet werden soll, &lt;br /&gt;
wird es aus der Datei &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; entfernt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  &amp;lt;body id=&amp;quot;app&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== App.vue ===&lt;br /&gt;
&lt;br /&gt;
Nun kann in die App eine Navigation eingebaut werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- /src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import AppNav       from &#039;/component/AppNav.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeI18n    from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  storeI18n();&lt;br /&gt;
&lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p      v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;AppNav v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;main   v-if=&amp;quot;session.isInitialized&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;router-view/&amp;gt;&lt;br /&gt;
  &amp;lt;/main&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
  @import &#039;/css/body.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zugehörige Navigationskomponente muss auch noch implementiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- /src/component/AppNav.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;nav&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/&amp;quot;&amp;gt;Home&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/hello&amp;quot;&amp;gt;Hello&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
  &amp;lt;/nav&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/AppNav.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* src/css/_config.css */&lt;br /&gt;
...&lt;br /&gt;
--background-color:  #C5EFFC;&lt;br /&gt;
--link-color:        #0027d4;&lt;br /&gt;
...&lt;br /&gt;
--nav-dist:          0.5em;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* src/css/AppNav.css */&lt;br /&gt;
&lt;br /&gt;
@import &#039;config.css&#039;;&lt;br /&gt;
&lt;br /&gt;
nav&lt;br /&gt;
{ top:      0;&lt;br /&gt;
  width:    100%;&lt;br /&gt;
  position: fixed;&lt;br /&gt;
&lt;br /&gt;
  ul&lt;br /&gt;
  { list-style-type: none;&lt;br /&gt;
    margin: 0 var(--nav-dist);&lt;br /&gt;
    padding: 0;&lt;br /&gt;
&lt;br /&gt;
    li&lt;br /&gt;
    { display: inline;&lt;br /&gt;
      a { padding: 0 var(--nav-dist); }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    a&lt;br /&gt;
    { text-decoration: none;&lt;br /&gt;
      color:           var(--link-color);&lt;br /&gt;
      font-weight:     bold;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    a:hover, .router-link-exact-active&lt;br /&gt;
    { text-decoration: underline;&lt;br /&gt;
      color:           var(--link-color);&lt;br /&gt;
      font-weight:     bold;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  label, input&lt;br /&gt;
  { margin: 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Navigation ===&lt;br /&gt;
Die Navigation muss in allen Komponenten, in den Seiten gewechselt werden, angepasst werden.&lt;br /&gt;
&lt;br /&gt;
Das ist in der App.vue der Fall (siehe zuvor) sowie in der ViewHome.vue.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- /src/vie/ViewHome.vue  --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
  import router        from &#039;/router&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    sayHello   = () =&amp;gt; router.push(&#039;/hello&#039;)&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Section-Verwaltung der ehemaligen Single-Page-Anwendung kann und sollte gelöscht werden:&lt;br /&gt;
* &amp;lt;code&amp;gt;config.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;storeSession.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ViewHome.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ViewHello.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; kann (samt CSS-Datei) ganz gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06a ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die Ausgabesprache soll vom Benutzer zur Laufzeit geändert werden können. Die Sprachen sollen durch Bilder (Flaggen) symbolisiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v06      # Wechsle in den Branch v06&lt;br /&gt;
git checkout -b v06a  # Klone v06 in einen neuen Branch v06a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Um die vorhandenen Sprachen durchblättern zu können, wird ein einfacher Automat verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
  &amp;quot;nextLanguage&amp;quot;:    {&amp;quot;de&amp;quot;: &amp;quot;en&amp;quot;, &amp;quot;en&amp;quot;: &amp;quot;de&amp;quot;}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== StoreI18n.js: Integration von nextLanguage ===&lt;br /&gt;
&lt;br /&gt;
Der Automat &amp;lt;code&amp;gt;nextLanguage&amp;lt;/code&amp;gt; wird in den StoreI18n integriert. Wenn die Methode &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; ohne Argument aufgerufen wird, soll der Automat die aktuelle Sprache &amp;lt;code&amp;gt;lang&amp;lt;/code&amp;gt; durch die Nachfolgersprache ersetzen.&lt;br /&gt;
Dazu sollte zunächst die zunächst Konstante &amp;lt;code&amp;gt;nextLanguage&amp;lt;/code&amp;gt; definiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
lang =&lt;br /&gt;
  ref(null),&lt;br /&gt;
&lt;br /&gt;
nextLanguage =&lt;br /&gt;
  reactive(config.nextLanguage),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese neue Konstante &amp;lt;code&amp;gt;nextLanguage&amp;lt;/code&amp;gt; wird verwendet, um die Sprache mittels &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; zu definieren, sofern&lt;br /&gt;
kein Argument übergeben wurde.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; soll bei einer Änderung der Sprache sowohl die notwendige JSON-Sprach-Datei laden als auch die in der HTML-Datei &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; ausgewiesene Sprache anpassen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/storeI18N.js&lt;br /&gt;
&lt;br /&gt;
changeLang =&lt;br /&gt;
  async (p_lang = null) =&amp;gt;&lt;br /&gt;
        { lang.value = p_lang ?? nextLanguage[lang.value];&lt;br /&gt;
          await getI18n(lang.value);&lt;br /&gt;
          document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
initialize = &lt;br /&gt;
  async resolve =&amp;gt; &lt;br /&gt;
        { await changeLang(config.defaultLanguage); &lt;br /&gt;
          resolve(); &lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bilder von Flaggen ===&lt;br /&gt;
&lt;br /&gt;
Zur Visualisierung der gewählten Sprache werden Bilder von Flaggen eingesetzt.&lt;br /&gt;
Wir kopieren die Bilder im SVG-Format aus dem NPM-Paket &amp;quot;https://github.com/lipis/flag-icons&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D flag-icons&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Ordner &amp;quot;node_modules/flag-icons/flags/4x3&amp;quot; finden Sie Flaggen zahlreicher Länder im 4-zu-3-Format.&lt;br /&gt;
Kopieren Sie die Flaggen der Länder Deutschland und Großbritanien in den Ordner &amp;lt;code&amp;gt;public/img&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In die Datei config.js wird der Pfad, unter dem die Bilder zu finden sind, eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
//   public/config.js&lt;br /&gt;
...&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:  &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;imgPath&amp;quot;:  &amp;quot;/img/$1.svg&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;: &amp;quot;/api/$1&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;FormButtonIcon&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Komponente wird benötigt: FormButtonIcon&lt;br /&gt;
Dabei handelt es sich um einen Button, der ein Icon anzeigt.&lt;br /&gt;
Das Bild kann zur Laufzeit geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import { computed, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    props       = defineProps&lt;br /&gt;
                  ({ image: { type: String },&lt;br /&gt;
                     width: { type: String, default: &#039;3em&#039;},&lt;br /&gt;
                  }),&lt;br /&gt;
    config      = inject(&#039;config&#039;),&lt;br /&gt;
    emit        = defineEmits([&#039;click&#039;]),&lt;br /&gt;
    click       = () =&amp;gt; emit(&#039;click&#039;),&lt;br /&gt;
    require     = v_url =&amp;gt; import.meta.url == null&lt;br /&gt;
                           ? v_url&lt;br /&gt;
                           : new URL(v_url, import.meta.url).href,&lt;br /&gt;
    imgPath     = config.imgPath,&lt;br /&gt;
    buttonImage = computed(() =&amp;gt; require(imgPath.replace(&#039;$1&#039;, props.image)))&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;input type=&amp;quot;image&amp;quot; :src=&amp;quot;buttonImage&amp;quot; :style=&amp;quot;{width}&amp;quot; @click=&amp;quot;click&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/form/FormButtonIcon.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
/* src/css/component/FormButtonIcon.css */&lt;br /&gt;
&lt;br /&gt;
@import &#039;FormButton.css&#039;;&lt;br /&gt;
&lt;br /&gt;
input&lt;br /&gt;
{ width:       $button_icon_width !important;&lt;br /&gt;
  margin-left: $nav-dist          !important;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fügen Sie in &amp;lt;code&amp;gt;src/css/_config.css&amp;lt;/code&amp;gt; noch &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
--button-icon-width: 1em;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ein.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterung von &amp;lt;code&amp;gt;AppNav.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Sprachwahl-Button muss noch in die Navigation eingebaut werden. In diesem Rahmen wird auch gleich die Navigation etwas internationalisiert.&lt;br /&gt;
&lt;br /&gt;
Ergänzen Sie in den Dictionaries der Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt;&lt;br /&gt;
folgende Attribute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
    ...         ...,&lt;br /&gt;
    &amp;quot;navHome&amp;quot;:  &amp;quot;Start&amp;quot;,&lt;br /&gt;
    &amp;quot;navHello&amp;quot;: &amp;quot;Hallo&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
    ...         ...,&lt;br /&gt;
    &amp;quot;navHome&amp;quot;:  &amp;quot;Home&amp;quot;,&lt;br /&gt;
    &amp;quot;navHello&amp;quot;: &amp;quot;Hello&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können Sie das Navigationsmenü internationalisieren (einschließlich Auswahl der Sprache per Button).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/component/AppNav.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButtonIcon from &#039;/component/form/FormButtonIcon.vue&#039;&lt;br /&gt;
  import storeI18n      from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const i18n = storeI18n()&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;nav&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/&amp;quot;&amp;gt;{{i18n.dictionary.navHome}}&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/hello&amp;quot;&amp;gt;{{i18n.dictionary.navHello}}&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;FormButtonIcon :image=&amp;quot;i18n.lang&amp;quot; @click=&amp;quot;i18n.changeLang()&amp;quot;/&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
  &amp;lt;/nav&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/AppNav.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06b ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Sorgen Sie dafür, dass &amp;lt;code&amp;gt;autofocus&amp;lt;/code&amp;gt; korrekt funktioniert, insbesondere beim Seitenwechsel via Navigation.&lt;br /&gt;
&lt;br /&gt;
Es ist sinnvoll, die Funktionalität in ein eigenes Store-Modul auszulagern, damit&lt;br /&gt;
das Session-Modul in Anwendungen, die keine Autofocus-Funktionalität benötigen, keinen&lt;br /&gt;
überflüssigen Code enthalten (Bertrand Meyer: Jedes Modul erfüllt genau eine Aufgabe).&lt;br /&gt;
Das Session-Modul wird entsprechend abgespeckt.&lt;br /&gt;
 &lt;br /&gt;
Der Autofocus-Store soll zwei Funktionen bieten:&lt;br /&gt;
* &amp;lt;code&amp;gt;setAutofocusId&amp;lt;/code&amp;gt;: Speichert die ID des Autofokus-Elements der aktuellen Seite (Default: &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;lt;code&amp;gt;setFocus&amp;lt;/code&amp;gt;: Setzt den Focus auf die aktuelle Autofokus-Komponente (sofern die ID ungleich &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; ist). Insbesondere für das Window-Ereignis &amp;lt;code&amp;gt;&#039;visibilitychange&#039;&amp;lt;/code&amp;gt; soll diese Funktion&lt;br /&gt;
aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/StoreAutofocus.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore } from &#039;pinia&#039;&lt;br /&gt;
import { ref }         from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeAutofocus =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;autofocus&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      c_autofocus_id = ref(),&lt;br /&gt;
      setFocus       = () =&amp;gt; document.getElementById(c_autofocus_id.value)?.focus(),&lt;br /&gt;
      setAutofocusId = p_autofocus_id =&amp;gt; { c_autofocus_id.value = p_autofocus_id; &lt;br /&gt;
                                           setFocus();&lt;br /&gt;
                                         }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;visibilitychange&#039;, setFocus);&lt;br /&gt;
&lt;br /&gt;
    return { setAutofocusId, setFocus }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeAutofocus&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore } from &#039;pinia&#039;&lt;br /&gt;
import { ref }         from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die aktuelle ID speichern zu können, muss das zugehörige Textfeld die Information, welche ID es hat, &lt;br /&gt;
per &amp;lt;code&amp;gt;emit&amp;lt;/code&amp;gt; an die Elternkomponente ausgeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
// /src/component/form/FormTextfield.vue&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
const&lt;br /&gt;
  props = defineProps&lt;br /&gt;
          ({ id: { type: String,  default: &#039;&#039; },&lt;br /&gt;
             ...&lt;br /&gt;
          })&lt;br /&gt;
  ... &lt;br /&gt;
  emit  = defineEmits([&#039;id&#039;, &#039;update:text&#039;, &#039;enter&#039;]),&lt;br /&gt;
...&lt;br /&gt;
emit(&#039;id&#039;, inputId);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen die Views, die ein Autofokus-Feld beinhalten, dieses an den Session-Store melden, &lt;br /&gt;
sobald sie per Routing aktiviert werden. Das ist in unserem Fall nur die &amp;lt;code&amp;gt;ViewHome&amp;lt;/code&amp;gt;.&lt;br /&gt;
Ein Watcher sorgt dafür, dass die ID des Textfeldes im Session-Objekt gespeichert wird, sobald &lt;br /&gt;
das zugehörige Textfeld die ID per &amp;lt;code&amp;gt;emit&amp;lt;/code&amp;gt; meldet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
// /src/view/ViewHome.vue&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import { ref, watch } from &#039;vue&#039;&lt;br /&gt;
  ...&lt;br /&gt;
  import storeAutofocus from &#039;/store/StoreAutofocus&#039;&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    id         = ref(null),&lt;br /&gt;
    autofocus  = storeAutofocus(),&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
  watch( id, () =&amp;gt; autofocus.setAutofocusId(id.value) )&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
        &amp;lt;FormTextfield @id=&amp;quot;setId&amp;quot;&lt;br /&gt;
          ...&lt;br /&gt;
        &amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schluss muss im Router noch die Autofocus-ID bei jedem Seitenwechsel gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/router/index.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import storeAutofocus from &#039;/store/StoreAutofocus&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
router.beforeEach(() =&amp;gt; storeAutofocus().setAutofocusId())&lt;br /&gt;
&lt;br /&gt;
export default router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist hier, dass man keine Konstante &amp;lt;code&amp;gt;session&amp;lt;/code&amp;gt; definieren kann, da die Stores erst nach dem Router-Objekt erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/router/index.js&lt;br /&gt;
...&lt;br /&gt;
import storeAutofocus from &#039;/store/StoreAutofocus&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  autofocus = storeAutofocus(), // FUNKTIONIERT NICHT&lt;br /&gt;
  ...&lt;br /&gt;
router.beforeEach(() =&amp;gt; autofocus.setAutofocusId())&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Funktionsaufruf &amp;lt;code&amp;gt;storeSession()&amp;lt;/code&amp;gt; darf daher erst in  der Callback-Funktion von &lt;br /&gt;
&amp;lt;code&amp;gt;router.beforeEach&amp;lt;/code&amp;gt; aufgerufen werden. Zu diesem Zeitpunkt sind die Stores und der Router auf jeden Fall schon initialisiert worden.&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss sollten Sie noch versuchen, &amp;lt;code&amp;gt;/src/component/AppNav.vue&amp;lt;/code&amp;gt; so abzuändern,&lt;br /&gt;
dass ein Klick auf die Fahne nicht nur die Sprache ändert, sonden auch noch den Focus auf das Startfeld setzt (such wenn dieses Feature vermutlich eher kontraproduktiv ist).&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06c ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Sorgen Sie dafür, dass die unterstützten Sprachen und der Sprach-Automat vom Backend&lt;br /&gt;
geladen wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v06b     # Wechsle in den Branch v06b&lt;br /&gt;
git checkout -b v06c  # Klone v06b in einen neuen Branch v06c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Splitten Sie die Config-Datei zunächst in zwei Teile: &amp;quot;config.json&amp;quot; wird aus dem Public-Ordner geladen.&lt;br /&gt;
&amp;quot;config_backend.json&amp;quot; wird normalerweise vom Backend geladen, kann aber zum Entwicklungszeitpunkt auch vom Public-Ordner geladen werden, wenn das Backend noch nicht stabil läuft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;configBackend&amp;quot;:  &amp;quot;/json/config_backend.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;imgPath&amp;quot;:        &amp;quot;/img/$1.svg&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:       &amp;quot;/api/$1&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/public/json/config_backend.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;languages&amp;quot;:    [&amp;quot;de&amp;quot;, &amp;quot;en&amp;quot;],&lt;br /&gt;
  &amp;quot;nextLanguage&amp;quot;: {&amp;quot;de&amp;quot;: &amp;quot;en&amp;quot;, &amp;quot;en&amp;quot;: &amp;quot;de&amp;quot;}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Backend teilt dem Frontend mit, welche Sprachen unterstützt werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Laden Sie, nachdem Sie &amp;quot;config&amp;quot; asynchron geladen haben, &amp;quot;configBackend&amp;quot; asynchron und kopieren Sie den Inhalt dieses Objekts in das Config-Objekt. Den Pfad von &amp;quot;configBackend&amp;quot; finden Sie unter &amp;quot;config.configBackend&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
const&lt;br /&gt;
  ...&lt;br /&gt;
  init = async () =&amp;gt;&lt;br /&gt;
         { const&lt;br /&gt;
             config        = await getJson(&#039;/json/config.json&#039;),&lt;br /&gt;
             configBackend = await getJson(config.configBackend)&lt;br /&gt;
&lt;br /&gt;
           Object.assign(config, configBackend)&lt;br /&gt;
&lt;br /&gt;
           app&lt;br /&gt;
             .provide(&#039;config&#039;, config)&lt;br /&gt;
             .use(pinia)&lt;br /&gt;
             .use(router)&lt;br /&gt;
             .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
         }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt es im Konfigurationsobjekt keine Defaultsprache mehr.&lt;br /&gt;
Im I18N-Store wird daher ab sofort die erste Sprache in der Liste der&lt;br /&gt;
unterstützten Sprachen als Defaultsprache genutzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/storeI18N.js&lt;br /&gt;
&lt;br /&gt;
initialize = &lt;br /&gt;
  async resolve =&amp;gt; &lt;br /&gt;
        { await changeLang(config.languages[0]); &lt;br /&gt;
          resolve(); &lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06d ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Defaultmäßig soll dem Benutzer die App in der Sprache angezeigt werden, die der Benutzer&lt;br /&gt;
im Browser voreingestellt hat. Sollte die voreingestellte Sprache nicht unterstützt werden, wird die App in der Defaultsprache angezeigt.&lt;br /&gt;
&lt;br /&gt;
Legen Sie zunächst wieder einen neuen Branch an.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v06c     # Wechsle in den Branch v06c&lt;br /&gt;
git checkout -b v06d  # Klone v06c in einen neuen Branch v06d&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Vorgehen ist im Prinzip ganz einfach.&lt;br /&gt;
&lt;br /&gt;
Mittels des NPM-Packets &amp;quot;navigator-languages&amp;quot; ermittelt man die vom Browser unterstützten&lt;br /&gt;
Sprachen.  In der Konfigurationsdatei listet man die Sprachen auf, die von der App unterstützt werden.&lt;br /&gt;
Besser wäre es natürlich, diese Liste vom Backend zu laden, damit die App mit bekommt, wenn neue&lt;br /&gt;
Sprachen zur Verfügung stehen. Am aufwändigsten ist es, diese beiden Array zu matchen.&lt;br /&gt;
&lt;br /&gt;
Mögliche Erweiterung: Auch die HTML-Pfade sollten internationalisiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm i navigator-languages // unterstützt mehrere Browservarianten&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Language Matching ===&lt;br /&gt;
&lt;br /&gt;
Das Language Matching erfolgt zweimal mit zwei verschachtelten Schleifen.&lt;br /&gt;
Zunächst wird überprüft, ob es exakte Übereinstimmungen gibt. Sollte das nicht der Fall sein, wird überprüft, ob es Matches zwischen den ersten beiden Buchstaben gibt (&#039;de&#039;, &#039;de-AT&#039;, &#039;de-DE&#039; matchen in diesem Schritt alle miteinander).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/util/chooseLanguage.js&lt;br /&gt;
&lt;br /&gt;
import navigatorLanguages from &#039;navigator-languages&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  /**&lt;br /&gt;
   * @function&lt;br /&gt;
   * @param  { Array&amp;lt;string&amp;gt; } p_langs_available &lt;br /&gt;
   *         An array of language strings containing the languages&lt;br /&gt;
   *         supported by the app &lt;br /&gt;
   * @param  { Array&amp;lt;string&amp;gt; } [p_langs_required  = navigatorLanguages()]&lt;br /&gt;
   *         An array of language strings containing the languages&lt;br /&gt;
   *         requested by the client &lt;br /&gt;
   * @return { string } &lt;br /&gt;
   *         The language to be displayed&lt;br /&gt;
   */&lt;br /&gt;
  chooseLanguage =&lt;br /&gt;
  (p_langs_available, p_langs_required = navigatorLanguages()) =&amp;gt;&lt;br /&gt;
  { let&lt;br /&gt;
      lang = p_langs_available[0],&lt;br /&gt;
      i=0, j=0, n=p_langs_required.length, m=p_langs_available.length&lt;br /&gt;
  &lt;br /&gt;
    outer:&lt;br /&gt;
    { while (i&amp;lt;n)&lt;br /&gt;
      { const c_lang_required = p_langs_required[i]&lt;br /&gt;
        while (j&amp;lt;m)&lt;br /&gt;
        { const c_lang_availabe = p_langs_available[j]&lt;br /&gt;
          if (c_lang_availabe === c_lang_required)&lt;br /&gt;
          // both languages match perfectly&lt;br /&gt;
          { lang = c_lang_availabe;&lt;br /&gt;
            break outer;&lt;br /&gt;
          }&lt;br /&gt;
          j++&lt;br /&gt;
        }&lt;br /&gt;
        i++&lt;br /&gt;
      }&lt;br /&gt;
      i=0, j=0&lt;br /&gt;
      while (i&amp;lt;n)&lt;br /&gt;
      { const c_lang_required = p_langs_required[i]&lt;br /&gt;
        while (j&amp;lt;m)&lt;br /&gt;
        { const c_lang_availabe = p_langs_available[j]&lt;br /&gt;
          if (c_lang_availabe.substring(0,2) === c_lang_required.substring(0,2))&lt;br /&gt;
          // main part of the languages match&lt;br /&gt;
          { lang = c_lang_availabe;&lt;br /&gt;
            break outer;&lt;br /&gt;
          }&lt;br /&gt;
          j++&lt;br /&gt;
        }&lt;br /&gt;
        i++&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  &lt;br /&gt;
     return lang&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
export default chooseLanguage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/store/StoreI18n&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Im I18N-Store wird nun beim Initialisieren nicht nicht mehr eine Defaultspache geladen, sondern &lt;br /&gt;
die Sprache, die möglichst nah an den Wünschen des im Browser des Benutzers gespeicherten Sprachwünschen liegt.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass Sie in die Datei &amp;lt;code&amp;gt;vite.config.common.js&amp;lt;/code&amp;gt; einen geeigneten Alias für den &lt;br /&gt;
&amp;lt;code&amp;gt;util&amp;lt;/code&amp;gt;-Pfad definieren müssen, um &amp;lt;code&amp;gt;&#039;/util/chooseLanguage&#039;&amp;lt;/code&amp;gt; an Stelle von&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/util/chooseLanguage&#039;&amp;lt;/code&amp;gt; schreiben zu können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
import chooseLanguage from &#039;/util/chooseLanguage&#039;&lt;br /&gt;
...&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          ...&lt;br /&gt;
          initialize = &lt;br /&gt;
             async resolve =&amp;gt; &lt;br /&gt;
                   { await changeLang(chooseLanguage(config.languages)); &lt;br /&gt;
                     resolve(); &lt;br /&gt;
                   }&lt;br /&gt;
        ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 07|Teil 7 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53250</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53250"/>
		<updated>2025-05-13T15:14:50Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Backend-Server mittels Express */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05a erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     # Wechsle in den Branch v05b&lt;br /&gt;
git checkout -b v05c  # Klone v05b in einen neuen Branch v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den I18N-Store werden folgende Funktionen ergänzt:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem.&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Inhalt von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; vereinfacht sich entsprechend.&lt;br /&gt;
Diese Datei nur noch Funktionen zur Verfügung, der in der Phrase &amp;lt;code&amp;gt;phrases.hello&amp;lt;/code&amp;gt;&lt;br /&gt;
den String &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt; durch einen Namen ersetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }             from &#039;pinia&#039;&lt;br /&gt;
import { ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
import StoreI18n                   from &#039;./StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
const storeGreeting =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;greeting&#039;,&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      i18n =&lt;br /&gt;
        StoreI18n(),&lt;br /&gt;
&lt;br /&gt;
      phrases =&lt;br /&gt;
        i18n.phrases,&lt;br /&gt;
&lt;br /&gt;
      dictionary =&lt;br /&gt;
        i18n.dictionary,&lt;br /&gt;
&lt;br /&gt;
      f_hello =&lt;br /&gt;
        p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Initiaisierung durch &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden, da das asynchrone Laden von Initialisierungsdaten in jedem Store selbst geregelt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung der einzelnen Stores erfolgt in der Hauptdatei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeI18n    from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
  &lt;br /&gt;
  // Activate initializer of &amp;quot;storeI18n&amp;quot;&lt;br /&gt;
  storeI18n(); // First call of storeI18n(): The code of the function is performed&lt;br /&gt;
               // All other calls: The reult of the first call is returned (Singleton)&lt;br /&gt;
&lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05c) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
=== express_hello_world ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Backend-Server mittels Express ===&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53249</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53249"/>
		<updated>2025-05-13T15:13:40Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* express_hello_world (v05d) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05a erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     # Wechsle in den Branch v05b&lt;br /&gt;
git checkout -b v05c  # Klone v05b in einen neuen Branch v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den I18N-Store werden folgende Funktionen ergänzt:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem.&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Inhalt von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; vereinfacht sich entsprechend.&lt;br /&gt;
Diese Datei nur noch Funktionen zur Verfügung, der in der Phrase &amp;lt;code&amp;gt;phrases.hello&amp;lt;/code&amp;gt;&lt;br /&gt;
den String &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt; durch einen Namen ersetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }             from &#039;pinia&#039;&lt;br /&gt;
import { ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
import StoreI18n                   from &#039;./StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
const storeGreeting =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;greeting&#039;,&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      i18n =&lt;br /&gt;
        StoreI18n(),&lt;br /&gt;
&lt;br /&gt;
      phrases =&lt;br /&gt;
        i18n.phrases,&lt;br /&gt;
&lt;br /&gt;
      dictionary =&lt;br /&gt;
        i18n.dictionary,&lt;br /&gt;
&lt;br /&gt;
      f_hello =&lt;br /&gt;
        p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Initiaisierung durch &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden, da das asynchrone Laden von Initialisierungsdaten in jedem Store selbst geregelt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung der einzelnen Stores erfolgt in der Hauptdatei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeI18n    from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
  &lt;br /&gt;
  // Activate initializer of &amp;quot;storeI18n&amp;quot;&lt;br /&gt;
  storeI18n(); // First call of storeI18n(): The code of the function is performed&lt;br /&gt;
               // All other calls: The reult of the first call is returned (Singleton)&lt;br /&gt;
&lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05c) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
=== express_hello_world ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Backend-Server mittels Express ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53248</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53248"/>
		<updated>2025-05-13T15:13:18Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Benutzung von Express als Backend-Server (v05d) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05a erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     # Wechsle in den Branch v05b&lt;br /&gt;
git checkout -b v05c  # Klone v05b in einen neuen Branch v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den I18N-Store werden folgende Funktionen ergänzt:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem.&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Inhalt von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; vereinfacht sich entsprechend.&lt;br /&gt;
Diese Datei nur noch Funktionen zur Verfügung, der in der Phrase &amp;lt;code&amp;gt;phrases.hello&amp;lt;/code&amp;gt;&lt;br /&gt;
den String &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt; durch einen Namen ersetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }             from &#039;pinia&#039;&lt;br /&gt;
import { ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
import StoreI18n                   from &#039;./StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
const storeGreeting =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;greeting&#039;,&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      i18n =&lt;br /&gt;
        StoreI18n(),&lt;br /&gt;
&lt;br /&gt;
      phrases =&lt;br /&gt;
        i18n.phrases,&lt;br /&gt;
&lt;br /&gt;
      dictionary =&lt;br /&gt;
        i18n.dictionary,&lt;br /&gt;
&lt;br /&gt;
      f_hello =&lt;br /&gt;
        p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Initiaisierung durch &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden, da das asynchrone Laden von Initialisierungsdaten in jedem Store selbst geregelt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung der einzelnen Stores erfolgt in der Hauptdatei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeI18n    from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
  &lt;br /&gt;
  // Activate initializer of &amp;quot;storeI18n&amp;quot;&lt;br /&gt;
  storeI18n(); // First call of storeI18n(): The code of the function is performed&lt;br /&gt;
               // All other calls: The reult of the first call is returned (Singleton)&lt;br /&gt;
&lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05c) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== express_hello_world (v05d) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Backend-Server mittels Express ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53247</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53247"/>
		<updated>2025-05-13T15:12:09Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Benutzung eines Backend-Servers (v05d) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05a erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     # Wechsle in den Branch v05b&lt;br /&gt;
git checkout -b v05c  # Klone v05b in einen neuen Branch v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den I18N-Store werden folgende Funktionen ergänzt:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem.&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Inhalt von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; vereinfacht sich entsprechend.&lt;br /&gt;
Diese Datei nur noch Funktionen zur Verfügung, der in der Phrase &amp;lt;code&amp;gt;phrases.hello&amp;lt;/code&amp;gt;&lt;br /&gt;
den String &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt; durch einen Namen ersetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }             from &#039;pinia&#039;&lt;br /&gt;
import { ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
import StoreI18n                   from &#039;./StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
const storeGreeting =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;greeting&#039;,&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      i18n =&lt;br /&gt;
        StoreI18n(),&lt;br /&gt;
&lt;br /&gt;
      phrases =&lt;br /&gt;
        i18n.phrases,&lt;br /&gt;
&lt;br /&gt;
      dictionary =&lt;br /&gt;
        i18n.dictionary,&lt;br /&gt;
&lt;br /&gt;
      f_hello =&lt;br /&gt;
        p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Initiaisierung durch &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden, da das asynchrone Laden von Initialisierungsdaten in jedem Store selbst geregelt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung der einzelnen Stores erfolgt in der Hauptdatei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeI18n    from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
  &lt;br /&gt;
  // Activate initializer of &amp;quot;storeI18n&amp;quot;&lt;br /&gt;
  storeI18n(); // First call of storeI18n(): The code of the function is performed&lt;br /&gt;
               // All other calls: The reult of the first call is returned (Singleton)&lt;br /&gt;
&lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05c) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== express_hello_world (v05d) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53246</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53246"/>
		<updated>2025-05-13T14:59:02Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* main.js */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05a erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     # Wechsle in den Branch v05b&lt;br /&gt;
git checkout -b v05c  # Klone v05b in einen neuen Branch v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den I18N-Store werden folgende Funktionen ergänzt:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem.&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Inhalt von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; vereinfacht sich entsprechend.&lt;br /&gt;
Diese Datei nur noch Funktionen zur Verfügung, der in der Phrase &amp;lt;code&amp;gt;phrases.hello&amp;lt;/code&amp;gt;&lt;br /&gt;
den String &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt; durch einen Namen ersetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }             from &#039;pinia&#039;&lt;br /&gt;
import { ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
import StoreI18n                   from &#039;./StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
const storeGreeting =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;greeting&#039;,&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      i18n =&lt;br /&gt;
        StoreI18n(),&lt;br /&gt;
&lt;br /&gt;
      phrases =&lt;br /&gt;
        i18n.phrases,&lt;br /&gt;
&lt;br /&gt;
      dictionary =&lt;br /&gt;
        i18n.dictionary,&lt;br /&gt;
&lt;br /&gt;
      f_hello =&lt;br /&gt;
        p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Initiaisierung durch &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden, da das asynchrone Laden von Initialisierungsdaten in jedem Store selbst geregelt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung der einzelnen Stores erfolgt in der Hauptdatei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeI18n    from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
  &lt;br /&gt;
  // Activate initializer of &amp;quot;storeI18n&amp;quot;&lt;br /&gt;
  storeI18n(); // First call of storeI18n(): The code of the function is performed&lt;br /&gt;
               // All other calls: The reult of the first call is returned (Singleton)&lt;br /&gt;
&lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== express_hello_world (v05d) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53245</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53245"/>
		<updated>2025-05-13T14:55:38Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* StoreI18n.js (v05c) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05a erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     # Wechsle in den Branch v05b&lt;br /&gt;
git checkout -b v05c  # Klone v05b in einen neuen Branch v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den I18N-Store werden folgende Funktionen ergänzt:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem.&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Inhalt von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; vereinfacht sich entsprechend.&lt;br /&gt;
Diese Datei nur noch Funktionen zur Verfügung, der in der Phrase &amp;lt;code&amp;gt;phrases.hello&amp;lt;/code&amp;gt;&lt;br /&gt;
den String &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt; durch einen Namen ersetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }             from &#039;pinia&#039;&lt;br /&gt;
import { ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
import StoreI18n                   from &#039;./StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
const storeGreeting =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;greeting&#039;,&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      i18n =&lt;br /&gt;
        StoreI18n(),&lt;br /&gt;
&lt;br /&gt;
      phrases =&lt;br /&gt;
        i18n.phrases,&lt;br /&gt;
&lt;br /&gt;
      dictionary =&lt;br /&gt;
        i18n.dictionary,&lt;br /&gt;
&lt;br /&gt;
      f_hello =&lt;br /&gt;
        p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden, da das asynchrone Laden von Initialisierungsdaten in jedem Store selbst geregelt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== express_hello_world (v05d) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53244</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53244"/>
		<updated>2025-05-13T14:53:27Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* main.js */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05a erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; (v05c) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     # Wechsle in den Branch v05b&lt;br /&gt;
git checkout -b v05c  # Klone v05b in einen neuen Branch v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den I18N-Store werden folgende Funktionen ergänzt:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem.&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Inhalt von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; vereinfacht sich entsprechend.&lt;br /&gt;
Diese Datei nur noch Funktionen zur Verfügung, der in der Phrase &amp;lt;code&amp;gt;phrases.hello&amp;lt;/code&amp;gt;&lt;br /&gt;
den String &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt; durch einen Namen ersetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }             from &#039;pinia&#039;&lt;br /&gt;
import { ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
import StoreI18n                   from &#039;./StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
const storeGreeting =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;greeting&#039;,&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      i18n =&lt;br /&gt;
        StoreI18n(),&lt;br /&gt;
&lt;br /&gt;
      phrases =&lt;br /&gt;
        i18n.phrases,&lt;br /&gt;
&lt;br /&gt;
      dictionary =&lt;br /&gt;
        i18n.dictionary,&lt;br /&gt;
&lt;br /&gt;
      f_hello =&lt;br /&gt;
        p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden, da das asynchrone Laden von Initialisierungsdaten in jedem Store selbst geregelt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== express_hello_world (v05d) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53243</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53243"/>
		<updated>2025-05-13T14:51:56Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* StoreI18n.js (v05c) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05a erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; (v05c) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     # Wechsle in den Branch v05b&lt;br /&gt;
git checkout -b v05c  # Klone v05b in einen neuen Branch v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den I18N-Store werden folgende Funktionen ergänzt:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem.&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden, da das asynchrone Laden von Initialisierungsdaten in jedem Store selbst geregelt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== express_hello_world (v05d) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53242</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53242"/>
		<updated>2025-05-13T14:50:31Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* StoreI18n.js (v05c) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05a erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; (v05c) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     # Wechsle in den Branch v05b&lt;br /&gt;
git checkout -b v05c  # Klone v05b in einen neuen Branch v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den I18N-Store werden folgende Funktionen ergänzt:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem.&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden, da das asynchrone Laden von Initialisierungsdaten in jedem Store selbst geregelt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== express_hello_world (v05d) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53241</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53241"/>
		<updated>2025-05-13T14:49:52Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* StoreGreeting.js */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05a erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; (v05c) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     # Wechsle in den Branch v05b&lt;br /&gt;
git checkout -b v05c  # Klone v05b in einen neuen Branch v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den I18N-Store werden folgende Funktionen ergänzt:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem.&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden, da das asynchrone Laden von Initialisierungsdaten in jedem Store selbst geregelt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== express_hello_world (v05d) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53240</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53240"/>
		<updated>2025-05-13T14:43:54Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Verallgemeinerung des Initialisierungsprozesses (v05b) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05a erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; (v05c) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     # Wechsle in den Branch v05b&lt;br /&gt;
git checkout -b v05c  # Klone v05b in einen neuen Branch v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den I18N-Store werden folgende Funktionen ergänzt:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem.&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden, da das asynchrone Laden von Initialisierungsdaten in jedem Store selbst geregelt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Inhalt von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; vereinfacht sich entsprechend.&lt;br /&gt;
Diese Datei nur noch Funktionen zur Verfügung, der in der Phrase &amp;lt;code&amp;gt;phrases.hello&amp;lt;/code&amp;gt;&lt;br /&gt;
den String &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt; durch einen Namen ersetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }             from &#039;pinia&#039;&lt;br /&gt;
import { ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
import StoreI18n                   from &#039;./StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
const storeGreeting =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;greeting&#039;,&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      i18n =&lt;br /&gt;
        StoreI18n(),&lt;br /&gt;
&lt;br /&gt;
      phrases =&lt;br /&gt;
        i18n.phrases,&lt;br /&gt;
&lt;br /&gt;
      dictionary =&lt;br /&gt;
        i18n.dictionary,&lt;br /&gt;
&lt;br /&gt;
      f_hello =&lt;br /&gt;
        p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== express_hello_world (v05d) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53239</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53239"/>
		<updated>2025-05-13T14:42:19Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* main.js */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; (v05c) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     # Wechsle in den Branch v05b&lt;br /&gt;
git checkout -b v05c  # Klone v05b in einen neuen Branch v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den I18N-Store werden folgende Funktionen ergänzt:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem.&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden, da das asynchrone Laden von Initialisierungsdaten in jedem Store selbst geregelt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Inhalt von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; vereinfacht sich entsprechend.&lt;br /&gt;
Diese Datei nur noch Funktionen zur Verfügung, der in der Phrase &amp;lt;code&amp;gt;phrases.hello&amp;lt;/code&amp;gt;&lt;br /&gt;
den String &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt; durch einen Namen ersetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }             from &#039;pinia&#039;&lt;br /&gt;
import { ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
import StoreI18n                   from &#039;./StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
const storeGreeting =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;greeting&#039;,&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      i18n =&lt;br /&gt;
        StoreI18n(),&lt;br /&gt;
&lt;br /&gt;
      phrases =&lt;br /&gt;
        i18n.phrases,&lt;br /&gt;
&lt;br /&gt;
      dictionary =&lt;br /&gt;
        i18n.dictionary,&lt;br /&gt;
&lt;br /&gt;
      f_hello =&lt;br /&gt;
        p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== express_hello_world (v05d) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53238</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53238"/>
		<updated>2025-05-13T14:40:04Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* StoreI18n.js */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; (v05c) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     # Wechsle in den Branch v05b&lt;br /&gt;
git checkout -b v05c  # Klone v05b in einen neuen Branch v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den I18N-Store werden folgende Funktionen ergänzt:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem.&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Inhalt von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; vereinfacht sich entsprechend.&lt;br /&gt;
Diese Datei nur noch Funktionen zur Verfügung, der in der Phrase &amp;lt;code&amp;gt;phrases.hello&amp;lt;/code&amp;gt;&lt;br /&gt;
den String &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt; durch einen Namen ersetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }             from &#039;pinia&#039;&lt;br /&gt;
import { ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
import StoreI18n                   from &#039;./StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
const storeGreeting =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;greeting&#039;,&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      i18n =&lt;br /&gt;
        StoreI18n(),&lt;br /&gt;
&lt;br /&gt;
      phrases =&lt;br /&gt;
        i18n.phrases,&lt;br /&gt;
&lt;br /&gt;
      dictionary =&lt;br /&gt;
        i18n.dictionary,&lt;br /&gt;
&lt;br /&gt;
      f_hello =&lt;br /&gt;
        p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== express_hello_world (v05d) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53237</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53237"/>
		<updated>2025-05-13T14:35:58Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Verallgemeinerung des Initialisierungsprozesses (v05c) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Ergänzt wurden:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Inhalt von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; vereinfacht sich entsprechend.&lt;br /&gt;
Diese Datei nur noch Funktionen zur Verfügung, der in der Phrase &amp;lt;code&amp;gt;phrases.hello&amp;lt;/code&amp;gt;&lt;br /&gt;
den String &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt; durch einen Namen ersetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }             from &#039;pinia&#039;&lt;br /&gt;
import { ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
import StoreI18n                   from &#039;./StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
const storeGreeting =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;greeting&#039;,&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      i18n =&lt;br /&gt;
        StoreI18n(),&lt;br /&gt;
&lt;br /&gt;
      phrases =&lt;br /&gt;
        i18n.phrases,&lt;br /&gt;
&lt;br /&gt;
      dictionary =&lt;br /&gt;
        i18n.dictionary,&lt;br /&gt;
&lt;br /&gt;
      f_hello =&lt;br /&gt;
        p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== express_hello_world (v05d) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53236</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53236"/>
		<updated>2025-05-13T14:33:21Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Verallgemeinerung des Initialisierungsprozesses (v05b) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05c) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     # Wechsle in den Branch v05b&lt;br /&gt;
git checkout -b v05c  # Klone v05b in einen neuen Branch v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Ergänzt wurden:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Inhalt von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; vereinfacht sich entsprechend.&lt;br /&gt;
Diese Datei nur noch Funktionen zur Verfügung, der in der Phrase &amp;lt;code&amp;gt;phrases.hello&amp;lt;/code&amp;gt;&lt;br /&gt;
den String &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt; durch einen Namen ersetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }             from &#039;pinia&#039;&lt;br /&gt;
import { ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
import StoreI18n                   from &#039;./StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
const storeGreeting =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;greeting&#039;,&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      i18n =&lt;br /&gt;
        StoreI18n(),&lt;br /&gt;
&lt;br /&gt;
      phrases =&lt;br /&gt;
        i18n.phrases,&lt;br /&gt;
&lt;br /&gt;
      dictionary =&lt;br /&gt;
        i18n.dictionary,&lt;br /&gt;
&lt;br /&gt;
      f_hello =&lt;br /&gt;
        p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== express_hello_world (v05d) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53235</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53235"/>
		<updated>2025-05-13T14:32:35Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Benutzung eines Backend-Servers (v05c) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Ergänzt wurden:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Inhalt von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; vereinfacht sich entsprechend.&lt;br /&gt;
Diese Datei nur noch Funktionen zur Verfügung, der in der Phrase &amp;lt;code&amp;gt;phrases.hello&amp;lt;/code&amp;gt;&lt;br /&gt;
den String &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt; durch einen Namen ersetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }             from &#039;pinia&#039;&lt;br /&gt;
import { ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
import StoreI18n                   from &#039;./StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
const storeGreeting =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;greeting&#039;,&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      i18n =&lt;br /&gt;
        StoreI18n(),&lt;br /&gt;
&lt;br /&gt;
      phrases =&lt;br /&gt;
        i18n.phrases,&lt;br /&gt;
&lt;br /&gt;
      dictionary =&lt;br /&gt;
        i18n.dictionary,&lt;br /&gt;
&lt;br /&gt;
      f_hello =&lt;br /&gt;
        p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== express_hello_world (v05d) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53234</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 05</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05&amp;diff=53234"/>
		<updated>2025-05-13T08:43:13Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Benutzung von Strapi als Backend-Servers (v05e) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v05&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05b&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v05e&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten, zweiten und dritten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle zunächst nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird die Anwendung allerdings erweitert. Die Begrüßung erfolgt in der vom Browser als Favorit angegebenen Sprache, sofern die Begrüßungsdaten für diese Sprache hinterlegt wurden. Anderenfalls erfolgt die Begrüßung ein der Defaultsprache.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v04     # Wechsle in den Branch v04&lt;br /&gt;
git checkout -b v05  # Klone v04 in einen neuen Branch v05&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kofigurationsdateien ==&lt;br /&gt;
&lt;br /&gt;
=== Hauptkonfigurationsdatei ===&lt;br /&gt;
Die folgende Datei dient zur statischen Konfiguration der Anwendung. &lt;br /&gt;
Der Inhalt wird in die App fest eingebunden und kann in der laufenden Anwendung ohnen einen Rebuild nicht geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;: &amp;quot;form&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Dateien sollen zunächst statisch eingelesen werden.&lt;br /&gt;
Sie dienen der Internationalisierung (internationalization = i..................n = i18n).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_de.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;Ihr Name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Fremder&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;Reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/json/i18n_en.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;phrases&amp;quot;:&lt;br /&gt;
  { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
  { &amp;quot;placeholderName&amp;quot;: &amp;quot;your name&amp;quot;,&lt;br /&gt;
    &amp;quot;stranger&amp;quot;:        &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
    &amp;quot;askName&amp;quot;:         &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
    &amp;quot;welcome&amp;quot;:         &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonReset&amp;quot;:     &amp;quot;reset&amp;quot;,&lt;br /&gt;
    &amp;quot;buttonSayHello&amp;quot;:  &amp;quot;say hello&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
== Statische JSON-Dateien (v05) ==&lt;br /&gt;
=== Initialisierung von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; mit Hilfe einer JSON-Datei ===&lt;br /&gt;
&lt;br /&gt;
Die JSON-Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; wird von &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; statisch eingelesen, um den zugehörigen Store zu initialisieren. Fügen Sie&lt;br /&gt;
folgenden Import-Befehl in diese Datei ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
import config from &#039;@/json/config.json&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle würde ich keine Vite-Alias &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; definieren, sondern die Schreibweise&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/json/...&#039;&amp;lt;/code&amp;gt; beibehalten. Der Grund ist, dass die JSON-Dateien später dynamisch aus dem Public-Bereich geladen werden sollen und der JSON-Ordner dort unter dem Namen &amp;lt;code&amp;gt;&#039;/json&#039;&amp;lt;/code&amp;gt; zu finden sein wird.&lt;br /&gt;
&lt;br /&gt;
Der statische Import von JSON-Dateien ist im ECMAScript-Standard nicht vorgesehen. Eigentlich müssen JSON-Dateien immer asynchron mit Hilfe spezieller AJAX-Anweisungen geladen werden.&lt;br /&gt;
&lt;br /&gt;
Allerdings ist der statische Import in Transcodern wie webpack, vite etc. möglich. Hierbei wird der Import-Befehl bereits zur „Übersetzungszeit“ und nicht erst zur Laufzeit ausgeführt. Das heißt, der Inhalt der JSON-Datei wird in den erzeugten JavaScript-Code direkt eingefügt.&lt;br /&gt;
&lt;br /&gt;
Nun kann der Name der Start-Section initialisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
const&lt;br /&gt;
  section = ref(config.startSection),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Initialisierung von StoreSession erfolgt bislang in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt;mittels der Funition &amp;lt;code&amp;gt;session.init&amp;lt;/code&amp;gt;. Entfernen Sie in der Datei &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; die Init-Funktion und in der Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; den zugehörigen Aufruf der Init-Funktion.&lt;br /&gt;
&lt;br /&gt;
Sie können nun auch noch die Funktion &amp;quot;init&amp;quot; aus der Datei StoreSession.js entfernen, da die Initialisierung jetzt über JSON erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Webanwendung soll internationalisiert werden. Dazu muss &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; um Internationalisierungstexte erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;config&amp;lt;/code&amp;gt; wird wie in &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; definiert. &lt;br /&gt;
In &amp;lt;code&amp;gt;config.i18n&amp;lt;/code&amp;gt; sind Internationalisierungstexte enthalten. (Sie wurden in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; separat geladen und eingefügt.) Die Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; und die Komponenten, die diesen Store verwenden, müssen entsprechend erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; gegenüber den früheren Implementierungen einen leeren Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
durch den Namen &amp;lt;code&amp;gt;dictionary.stranger&amp;lt;/code&amp;gt; ersetzt. (Isabella, danke für den Tipp.)&lt;br /&gt;
&lt;br /&gt;
Beachten Sie außerdem, dass &amp;lt;code&amp;gt;f_hello&amp;lt;/code&amp;gt; früher &amp;lt;code&amp;gt;sayHello&amp;lt;/code&amp;gt; hieß. Allerdings gibt es in &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; &lt;br /&gt;
eine Funktion mit gleichem Namen, aber anderer Funktionalität. Um Verwechslungen zu vermeiden, habe ich die (Hilfs-)Funktion in   &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; umbenannt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }                       from &#039;pinia&#039;&lt;br /&gt;
import { reactive, ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
import config from &#039;@/json/i18n_de.json&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        phrases =&lt;br /&gt;
          reactive(config.phrases),&lt;br /&gt;
&lt;br /&gt;
        dictionary =&lt;br /&gt;
          reactive(config.dictionary),&lt;br /&gt;
&lt;br /&gt;
        name =&lt;br /&gt;
          ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
        //p_name =&amp;gt; phrases?.hello?.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(dictionary.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(name.value))/*,&lt;br /&gt;
&lt;br /&gt;
        askName =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.askName),&lt;br /&gt;
&lt;br /&gt;
        welcome =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.welcome),&lt;br /&gt;
&lt;br /&gt;
        buttonReset =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonReset),&lt;br /&gt;
&lt;br /&gt;
        buttonSayHello =&lt;br /&gt;
          computed(() =&amp;gt; dictionary.buttonSayHello)&lt;br /&gt;
        */&lt;br /&gt;
    &lt;br /&gt;
      return { name, helloStranger, hello, dictionary: readonly(dictionary), reset&lt;br /&gt;
               /*, askName, welcome, buttonReset, buttonSayHello */&lt;br /&gt;
             }&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Methoden askName, welcome etc. würden die Verwendung des Stores einfacher machen, blähen aber die Datei stark auf, wenn i18n viele Begriffe enthält.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss an die Internationalisierung angepasst werden, indem alle deutschen Wörter und Phrasen ersetzt durch Zugriffe auf den &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionHello.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;hello&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;{{dictionary.welcome}}&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/section/SectionHello.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;SectionForm.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;SectionHello.vue&amp;lt;/code&amp;gt; muss auf dieselbe Weise aktualisiert werden. Allerdings benötig man ein paar mehr Konstanten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/section/SectionForm.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    reset      = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello   = () =&amp;gt; session.section = &#039;hello&#039;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;!-- const in code: &#039;form&#039; should be passed via a prop --&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormTextfield :label=&amp;quot;dictionary.askName&amp;quot;&lt;br /&gt;
                       v-model:text=&amp;quot;greeting.name&amp;quot;&lt;br /&gt;
                       @enter=&amp;quot;sayHello&amp;quot;&lt;br /&gt;
                       autofocus&lt;br /&gt;
                       :placeholder=&amp;quot;dictionary.placeholderName&amp;quot;&lt;br /&gt;
        /&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonReset&amp;quot;    type=&amp;quot;reset&amp;quot;  @click=&amp;quot;reset&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;FormButton :label=&amp;quot;dictionary.buttonSayHello&amp;quot; type=&amp;quot;button&amp;quot; @click=&amp;quot;sayHello&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dynamisches Laden von JSON-Dateien (v05a) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfiguriere und internationalisiere die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
Dazu wird die Version v05 erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05      # Wechsle in den Branch v05&lt;br /&gt;
git checkout -b v05a  # Klone v05 in einen neuen Branch v05a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Internationalisierungsdateien ===&lt;br /&gt;
&lt;br /&gt;
Der JSON-Ordner &amp;lt;code&amp;gt;src/json&amp;lt;/code&amp;gt; wird in den Public-Bereich verschoben.&lt;br /&gt;
Er heißt nun &amp;lt;code&amp;gt;public/json&amp;lt;/code&amp;gt;. JSON-Datein, die sich im Public-Bereich befinden müssen dynamisch zur Laufzeit der App geladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Er können jederzeit analog zu &amp;lt;code&amp;gt;i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;i18n_en.json&amp;lt;/code&amp;gt; I18n-Dateien für weitere Sprachen definiert werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;getJson.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird eine Service-Funktion definiert zum asynchronen Laden von JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/service/getJson.js&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * @function&lt;br /&gt;
 * @param  { string } p_url - The URL of a JSON file zo be loaded&lt;br /&gt;
 * @return { JSON }   The content of the JSON file&lt;br /&gt;
 * @throws { Error }  If the file does not exists, an error is thrown&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
async function getJson(p_url)&lt;br /&gt;
{ return fetch(p_url)&lt;br /&gt;
    .then&lt;br /&gt;
     (response =&amp;gt;&lt;br /&gt;
      { if (response.ok)&lt;br /&gt;
        { return response.json() }&lt;br /&gt;
        else&lt;br /&gt;
        { throw new Error(`&#039;${response.url}&#039; not found`) }&lt;br /&gt;
      }&lt;br /&gt;
     )&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default getJson&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Globale Konfigurationsdatei ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              config = await getJson(&#039;/json/config.json&#039;)&lt;br /&gt;
&lt;br /&gt;
            config.i18n =&lt;br /&gt;
              await getJson(config.apiRoot.replace(&#039;$1&#039;, config.defaultLanguage))&lt;br /&gt;
&lt;br /&gt;
            app&lt;br /&gt;
              .provide(&#039;config&#039;, config)&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; könnte man die I18n-Daten abhängig von der in der Datei &amp;lt;code&amp;gt;/json/config.json&amp;lt;/code&amp;gt;&lt;br /&gt;
festgelegten Defaultsprache nachladen. Dieses muss mit einem zweiten &amp;lt;code&amp;gt;getJson&amp;lt;/code&amp;gt;-Befehl erfolgen, der ausgeführt wird, &#039;&#039;&#039;nachdem&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; vollständig geladen wurde.&lt;br /&gt;
&lt;br /&gt;
Besser ist es allerdings einen allgemeineren Initialisierungsmechanismus zu etablieren, mit dem man anhängig von &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; verschiedene Daten vom Backend lädt, um die Anwendung zu initialisieren. Das machen wir später im nächsten Schritt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wir holen uns nun in den Stores &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt;  und &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;&lt;br /&gt;
die Konfigurations-Infomationen aus dem von der Main initialisierten Config-Onjekt.&lt;br /&gt;
&lt;br /&gt;
Auf dieses können wir mit Hilde der Vue-Funktion &amp;lt;code&amp;gt;inject&amp;lt;/code&amp;gt; zugreifen.&lt;br /&gt;
&lt;br /&gt;
Der Iport-Befehl zum Laden der Konfigurationsdatei muss jeweils gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
...&lt;br /&gt;
  { const&lt;br /&gt;
      config    = inject(&#039;config&#039;), // contains the app configuration object&lt;br /&gt;
      ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; müssen wir noch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
durch&lt;br /&gt;
&amp;lt;code&amp;gt;config.i18n.phrases&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;config.dictionary&amp;lt;/code&amp;gt;&lt;br /&gt;
ersetzen.&lt;br /&gt;
&lt;br /&gt;
== Verallgemeinerung des Initialisierungsprozesses (v05b) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Der Initialisierungsprozess soll so verallgemeinert werden, dass &lt;br /&gt;
jedes Modul (i. Allg. verschiedene Stores) eine Initialisierungsfunktion definieren kann,&lt;br /&gt;
die zum Zeitpunkt des App-Starts ausgeführt wird. &lt;br /&gt;
&lt;br /&gt;
Als erste Anwendung erstellen wir dann einen Store &amp;lt;code&amp;gt;StoreI18N&amp;lt;/code&amp;gt; der die Internationalisierungsdaten per JSON lädt.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05a     # Wechsle in den Branch v05a&lt;br /&gt;
git checkout -b v05b  # Klone v05a in einen neuen Branch v05b&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Initialisierungsprozess wird vom Session-Store verwaltet. &lt;br /&gt;
Er stellt zu diesem Zweck drei Funktionen zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt;: Reaktive Variable, die &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; enthält, sobald der Initialisierungsprozess abgeschlossen wurde.&lt;br /&gt;
* &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt;: Fügt eine Initialisierungsfunktion hinzu, die durch den Initialisierungsprozess ausgeführt werden soll.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: Startet den Initialisierungsprozess und setzt  &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; zum Schluss auf &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Die Konstante &amp;lt;code&amp;gt;initializers&amp;lt;/code&amp;gt; enthält alle Initialisierungsfunktionen, die vor Start der Anwendung ausgeführt werden sollen.&lt;br /&gt;
Diese werden von anderen Stores mit Hilfe der Funktion &amp;lt;code&amp;gt;addInitializer&amp;lt;/code&amp;gt; hinzugefügt. Die Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; aktiviert die entsprechenden Stores und führt danach die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; aus. Mit Hilfe des reaktiven Konstante &amp;lt;code&amp;gt;isInitialized&amp;lt;/code&amp;gt; erfährt die &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;-Komponente, wann alle Stores initialisiert sind und kann dann vom Lade-Bildschirm auf den eigentlichen Startbildschirm umschalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore           } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      ...&lt;br /&gt;
&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { initAutofocus, section, setFocus, addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun kann man das Ganze mal ausprobieren. Solange der Ladevorgang dauert, &lt;br /&gt;
wird ein Ladescreen angezeigt. Hier wird der Ladevorgang durch einen Zwei-Sekunde-Timer simuliert.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import HelloWorld   from &#039;/component/HelloWorld.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  // fake initializer, in order to see the loading information for at least two seconds&lt;br /&gt;
  session.addInitializer(p_resolve =&amp;gt; setTimeout(p_resolve, 2000));&lt;br /&gt;
              &lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p          v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;HelloWorld v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt; übernimmt die I18n-Funktionalität von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt;. &lt;br /&gt;
Damit ist die Internationalisierung in einem anwendungsunabhängigen Modul verfügbar. &lt;br /&gt;
Das erhöht die Wiederverwendbarkeit dieses Stores.&lt;br /&gt;
&lt;br /&gt;
Ergänzt wurden:&lt;br /&gt;
* &amp;lt;code&amp;gt;getI18n&amp;lt;/code&amp;gt;: eine Funktion zum Laden der I18n-Daten von einem Backendsystem&lt;br /&gt;
* &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt;: eine Funktion zum asynchronen Ändern der Sprache.&lt;br /&gt;
* &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt;: eine Funktion zum Initialisieren des Stores.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;initialize&amp;lt;/code&amp;gt; lädt die I18n-Daten für die in der Config-Datei festgelegte &lt;br /&gt;
Defaultsprache. Diese Funktion wird im Session-Store als Initialisierungsfunktion registriert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreI18n.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }           from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive, inject } from &#039;vue&#039;&lt;br /&gt;
import getJson                   from &#039;/service/getJson&#039;&lt;br /&gt;
import storeSession              from &#039;/store/StoreSession&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeI18n =&lt;br /&gt;
    defineStore&lt;br /&gt;
    ( &#039;i18n&#039;,&lt;br /&gt;
&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          config =&lt;br /&gt;
            inject(&#039;config&#039;),&lt;br /&gt;
&lt;br /&gt;
          session =&lt;br /&gt;
            storeSession(),&lt;br /&gt;
&lt;br /&gt;
          lang =&lt;br /&gt;
            ref(null),&lt;br /&gt;
&lt;br /&gt;
          phrases /* Record&amp;lt;string, string&amp;gt; */ =&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          dictionary /* Record&amp;lt;string, string&amp;gt; */=&lt;br /&gt;
            reactive({}),&lt;br /&gt;
&lt;br /&gt;
          getI18n =&lt;br /&gt;
            async p_lang =&amp;gt;&lt;br /&gt;
                  { const&lt;br /&gt;
                      c_i18n_json =&lt;br /&gt;
                        await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value))&lt;br /&gt;
                    Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
                    Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          changeLang =&lt;br /&gt;
            async (p_lang = null) =&amp;gt;&lt;br /&gt;
                  { lang.value = p_lang ?? config.defaultLanguage;&lt;br /&gt;
                    await getI18n(lang.value);&lt;br /&gt;
                    document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
                  },&lt;br /&gt;
&lt;br /&gt;
          initialize =&lt;br /&gt;
             async resolve =&amp;gt;&lt;br /&gt;
                   { await changeLang(config.defaultLanguage);&lt;br /&gt;
                     resolve();&lt;br /&gt;
                   }&lt;br /&gt;
&lt;br /&gt;
        session.addInitializer(initialize);&lt;br /&gt;
&lt;br /&gt;
        return { lang, phrases, dictionary, changeLang }&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
export default storeI18n&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; enthält einen kleinen Hack, um die korrekte Sprache in das HTML-Tag der Datei &amp;quot;index.html&amp;quot; einzufügen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
document.getElementsByTagName(&#039;html&#039;)[0].lang = lang&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird im Document Tree der Datei &amp;quot;index.html&amp;quot; jeweils die aktuelle Sprache verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;de&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte, dass im Skript-Bereich des Moduls &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; der Store mittels &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; &lt;br /&gt;
erstmals aufgerufen muss, damit er initialisiert wird. Insbesondere wird dabei der Befehl &amp;lt;code&amp;gt;session.addInitializer(initialize)&amp;lt;/code&amp;gt;&lt;br /&gt;
ausgeführt, das heißt die Initialisierungsfunktion zum Session-Store hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Fügen Sie also den den Aufruf &amp;lt;code&amp;gt;storeI18n()&amp;lt;/code&amp;gt; samt zugehörigem Import-Befehl in die &lt;br /&gt;
Datei &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; ein. (Den Fake-Initializer können Sie deaktivieren, wenn Sie wollen. Er wird nicht mehr benötigt.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration in der Datei &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; kann nun vereinfacht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  pinia = createPinia(),&lt;br /&gt;
  app   = createApp(App),&lt;br /&gt;
  init  = async () =&amp;gt;&lt;br /&gt;
          { app&lt;br /&gt;
              .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
              .use(pinia)&lt;br /&gt;
              .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
          }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Inhalt von &amp;lt;code&amp;gt;StoreGreeting.js&amp;lt;/code&amp;gt; vereinfacht sich entsprechend.&lt;br /&gt;
Diese Datei nur noch Funktionen zur Verfügung, der in der Phrase &amp;lt;code&amp;gt;phrases.hello&amp;lt;/code&amp;gt;&lt;br /&gt;
den String &amp;lt;code&amp;gt;$1&amp;lt;/code&amp;gt; durch einen Namen ersetzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }             from &#039;pinia&#039;&lt;br /&gt;
import { ref, computed, readonly } from &#039;vue&#039;&lt;br /&gt;
import StoreI18n                   from &#039;./StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
const storeGreeting =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;greeting&#039;,&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      i18n =&lt;br /&gt;
        StoreI18n(),&lt;br /&gt;
&lt;br /&gt;
      phrases =&lt;br /&gt;
        i18n.phrases,&lt;br /&gt;
&lt;br /&gt;
      dictionary =&lt;br /&gt;
        i18n.dictionary,&lt;br /&gt;
&lt;br /&gt;
      f_hello =&lt;br /&gt;
        p_name =&amp;gt; phrases.hello.replace(&#039;$1&#039;, p_name === &#039;&#039; ? dictionary.stranger : p_name),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung eines Backend-Servers (v05c) ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die I18N-Daten sollen von einem Backend-Server gelesen werden.&lt;br /&gt;
&lt;br /&gt;
Dazu wird die Version v05b erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05b     &lt;br /&gt;
git checkout -b v05c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als ersten Backend-Server verwenden wir den JSON-Server, einen Fake-REST-Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D json-server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um ihn mittels &amp;quot;npm run server&amp;quot; zu starten, wird folgendes Skript in die Datei package.json eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;server&amp;quot;: &amp;quot;npx json-server --watch db.json --port 4000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;JSON-Server-Daten&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// db.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;v1&amp;quot;:&lt;br /&gt;
  [ { &amp;quot;id&amp;quot;: &amp;quot;de&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hallo, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Fremdling&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;Wie heißen Sie?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Willkommen zu Web-Programmierung!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;Reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;Begrüßung&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    { &amp;quot;id&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;phrases&amp;quot;:&lt;br /&gt;
      { &amp;quot;hello&amp;quot;: &amp;quot;Hello, $1!&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
&lt;br /&gt;
      &amp;quot;dictionary&amp;quot;:&lt;br /&gt;
      { &amp;quot;stranger&amp;quot;:       &amp;quot;Stranger&amp;quot;,&lt;br /&gt;
        &amp;quot;askName&amp;quot;:        &amp;quot;What&#039;s your name?&amp;quot;,&lt;br /&gt;
        &amp;quot;welcome&amp;quot;:        &amp;quot;Welcome to Web Programing!&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonReset&amp;quot;:    &amp;quot;reset&amp;quot;,&lt;br /&gt;
        &amp;quot;buttonSayHello&amp;quot;: &amp;quot;say hello&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie dann Ihren JSON-Server, indem Sie ihn starten und dann in der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;&lt;br /&gt;
auf die jeweiligen Send-Request-Anweisungen klicken:&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
npm run server&lt;br /&gt;
	&lt;br /&gt;
// test.rest&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/ HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/de HTTP/1.1&lt;br /&gt;
	&lt;br /&gt;
###&lt;br /&gt;
GET http://localhost:4000/v1/en HTTP/1.1 	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vue-Server als Proxy ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// vite.config.common.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
export default defineConfig&lt;br /&gt;
({...&lt;br /&gt;
  resolve:&lt;br /&gt;
  { ...&lt;br /&gt;
  },&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:4000/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api/, &#039;/v1&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Starten Sie nun beide Server und testen Sie die Hello-World-Anwendung Browser:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm run server  # 1. Terminal&lt;br /&gt;
npm run dev     # 2. Terminal&lt;br /&gt;
&lt;br /&gt;
http://localhost:5173/api&lt;br /&gt;
http://localhost:5173/api/de&lt;br /&gt;
http://localhost:5173/api/en&lt;br /&gt;
&lt;br /&gt;
http://localhost:4000/v1&lt;br /&gt;
http://localhost:4000/v1/de&lt;br /&gt;
http://localhost:4000/v1/en&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Vue-URLs funktionieren im Browser, aber im Gegensatz zu den JSON-Server-URLs nicht in &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt;, da Vue keine echte REST-API zur Verfügung stellt.&lt;br /&gt;
&lt;br /&gt;
=== Zugriff auf den REST-Server ===&lt;br /&gt;
&lt;br /&gt;
Um auf den neuen REST-Server zuzugreifen, reicht es,&lt;br /&gt;
die URL in der Datei &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; anzupassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt; werden nicht mehr benötigt.&lt;br /&gt;
&lt;br /&gt;
== express_hello_world (v05d) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir express_hello_world&lt;br /&gt;
cd express_hello_world&lt;br /&gt;
npm init -y&lt;br /&gt;
npm i express dotenv&lt;br /&gt;
npm i -D nodemon&lt;br /&gt;
&lt;br /&gt;
# Dateien erstellen/bearbeiten&lt;br /&gt;
npm run server&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// package.json&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;scripts&amp;quot;: {&lt;br /&gt;
 &amp;quot;server&amp;quot;: &amp;quot;nodemon -r dotenv/config src/server.js&amp;quot;,&lt;br /&gt;
 &amp;quot;prod&amp;quot;:   &amp;quot;node    -r dotenv/config src/server.js&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/index.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/index.html --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;head&amp;gt;&lt;br /&gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot;&lt;br /&gt;
          content=&amp;quot;width=device-width,&lt;br /&gt;
          initial-scale=1,&lt;br /&gt;
          shrink-to-fit=no&amp;quot;&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    &amp;lt;title&amp;gt;Express Hello World 01&amp;lt;/title&amp;gt;&lt;br /&gt;
  &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;&lt;br /&gt;
      &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/server.js&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000,&lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`&lt;br /&gt;
  }         = process.env,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
&lt;br /&gt;
c_app.get&lt;br /&gt;
(&#039;/&#039;,&lt;br /&gt;
 (req, res) =&amp;gt;&lt;br /&gt;
 { //console.log(c_dirname);&lt;br /&gt;
   res.sendFile(&#039;./index.html&#039;, {root: c_dirname});&lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
c_app.listen(PORT);&lt;br /&gt;
&lt;br /&gt;
console.log(`Running on ${SERVER}`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Express als Backend-Server (v05d) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie parallel zum Verzeichnis &amp;lt;code&amp;gt;frontend&amp;lt;/code&amp;gt; einen Ordner &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt; und kopieren Sie die Dateien aus dem Ordner &amp;lt;code&amp;gt;express_hello_world&amp;lt;/code&amp;gt; nach &amp;lt;code&amp;gt;backend&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Installieren Sie außerdem das NPM-Modul &amp;lt;code&amp;gt;morgan&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Kopieren Sie &#039;&#039;&#039;das Array&#039;&#039;&#039; aus der Datei &amp;lt;code&amp;gt;db.json&amp;lt;/code&amp;gt; der Version v05a&lt;br /&gt;
in die Datei &amp;lt;code&amp;gt;src/db.json&amp;lt;/code&amp;gt; und ersetzen Sie in dieser Datei zweimal &amp;lt;code&amp;gt;&amp;quot;id&amp;quot;&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;&amp;quot;lang&amp;lt;/code&amp;gt;. Den Inhalt dieser Datei geben wir nun mit Hilfe eines Express-Servers aus.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/server.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @author    Wolfgang Kowarschick &amp;lt;kowa@hs-augsburg.de&amp;gt;&lt;br /&gt;
 * @copyright 2016-2025&lt;br /&gt;
 * @license   MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
import express from &#039;express&#039;&lt;br /&gt;
import path    from &#039;path&#039;&lt;br /&gt;
import morgan  from &#039;morgan&#039;&lt;br /&gt;
&lt;br /&gt;
let db;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  { PORT    = 4000, &lt;br /&gt;
    SERVER  = `http://localhost:${PORT}`,&lt;br /&gt;
    DB      = `${SERVER}/db.json`&lt;br /&gt;
  }         = process.env ,&lt;br /&gt;
  c_app     = express(),&lt;br /&gt;
  c_dirname = path.resolve(path.dirname(process.argv[1]))&lt;br /&gt;
;&lt;br /&gt;
&lt;br /&gt;
// Middleware for debugging&lt;br /&gt;
c_app.use(morgan(&amp;quot;combined&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
c_app.use &lt;br /&gt;
( function (req, res, next) &lt;br /&gt;
  { console.log(req);&lt;br /&gt;
    next();&lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
c_app&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./index.html&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/db.json&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { res.sendFile(&#039;./db.json&#039;, {root: c_dirname }); }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; res.json(db)&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
    res.json(l_i18n); &lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
.get&lt;br /&gt;
( &#039;/v1/:lang/:type&#039;, &lt;br /&gt;
  (req, res) =&amp;gt; &lt;br /&gt;
  { //console.log(req.params.lang, req.params.type);&lt;br /&gt;
    const l_i18n = db.filter(i18n =&amp;gt; i18n.lang === req.params.lang)[0];&lt;br /&gt;
&lt;br /&gt;
    res.json(l_i18n[req.params.type]); &lt;br /&gt;
  }&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
async function init()&lt;br /&gt;
{ c_app.listen(PORT);&lt;br /&gt;
  db = (await fetch(DB).then(response =&amp;gt; response.json())).v1&lt;br /&gt;
  console.log(`Server is tunning on ${SERVER}`)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
init()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Testen Sie diese Datei mit Hilfe der Datei &amp;lt;code&amp;gt;test.rest&amp;lt;/code&amp;gt; aus dem Frontend. Erweitern Sie die Test-Datei sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Benutzung von Strapi als Backend-Servers (v05e) ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie im Root-Verzeichnis Ihrer App zunächst die Datei &amp;lt;code&amp;gt;create-dotenv.sh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env bash&lt;br /&gt;
&lt;br /&gt;
declare -A strapi=(&lt;br /&gt;
  [&amp;quot;dir&amp;quot;]=&amp;quot;backend_strapi&amp;quot;&lt;br /&gt;
  [&amp;quot;HOST&amp;quot;]=127.0.0.1&lt;br /&gt;
  [&amp;quot;PORT&amp;quot;]=1337&lt;br /&gt;
  [&amp;quot;APP_KEYS&amp;quot;]=&amp;quot;$(openssl rand -hex 64),$(openssl rand -hex 64)&amp;quot;&lt;br /&gt;
  [&amp;quot;API_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;ADMIN_JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;TRANSFER_TOKEN_SALT&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
  [&amp;quot;JWT_SECRET&amp;quot;]=$(openssl rand -hex 64)&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
create_env_variables(){&lt;br /&gt;
  for env in $1&lt;br /&gt;
  do&lt;br /&gt;
    declare -n dict=$env&lt;br /&gt;
    directory=${dict[dir]}&lt;br /&gt;
    cd $directory&lt;br /&gt;
    echo &amp;quot;Creating $env .env in $directory&amp;quot;&lt;br /&gt;
    [ ! -e .env ] || rm .env&lt;br /&gt;
    for var in &amp;quot;${!dict[@]}&amp;quot;&lt;br /&gt;
    do  &lt;br /&gt;
      [ &amp;quot;$var&amp;quot; != &amp;quot;dir&amp;quot; ] &amp;amp;&amp;amp; echo &amp;quot;$var=${dict[$var]}&amp;quot; &amp;gt;&amp;gt; .env&lt;br /&gt;
    done&lt;br /&gt;
    unset -n dict&lt;br /&gt;
    cd - &amp;gt;/dev/null&lt;br /&gt;
  done&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
create_env_variables &#039;strapi&#039;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;------------&amp;gt; $0 has finished successfully&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Installieren Sie Strapi gemäß den Vorgaben aus der Vorlesung (Miro) und&lt;br /&gt;
führen Sie dann den Befehl &amp;lt;code&amp;gt;./create-dotenv.sh&amp;lt;/code&amp;gt; in der Konsole aus.&lt;br /&gt;
Damit wird eine Dummy-Datei &amp;lt;code&amp;gt;strapi/.env&amp;lt;/code&amp;gt; erzeugt. Diesen Befehl müssen Sie jedesmal&lt;br /&gt;
ausführen, wenn Sie die Strapi-App aus dem Repository laden, da die Datei &amp;lt;code&amp;gt;.env&amp;lt;/code&amp;gt;&lt;br /&gt;
nicht im Repository gespeichert wird. Die generierte Datei enthält Zufallszahlen, &lt;br /&gt;
die für den sicheren Datenaustausch mit dem Frontend benötigt werden.&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie nun den Inhalt der Strapi-Datenbank gemäß den Vorgaben aus der Vorlesung (Miro).&lt;br /&gt;
&lt;br /&gt;
Um darauf vom Frontend aus zuzugreifen, müssen Sie drei Dateien anpassen:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Konfiguration&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /public/json/config.json: Api-Root&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Proxy&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
// /vite.config.js: Port 1337 und Rewrite-Regel&lt;br /&gt;
&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  server:&lt;br /&gt;
  { proxy:&lt;br /&gt;
    { &#039;/api&#039;:&lt;br /&gt;
      { target:       &#039;http://localhost:1337/&#039;,&lt;br /&gt;
        changeOrigin: true,&lt;br /&gt;
        rewrite:      path =&amp;gt; path.replace(/^\/api\//, &#039;/api/i18ns?filters[lang][$eq]=&#039;)&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Datenextraktion in &amp;lt;code&amp;gt;StoreI18n.js&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  getI18n =&lt;br /&gt;
    async p_lang =&amp;gt;&lt;br /&gt;
          { const&lt;br /&gt;
              c_api_data = &lt;br /&gt;
                await getJson(config.apiRoot.replace(&#039;$1&#039;, p_lang ?? lang.value)),&lt;br /&gt;
              c_i18n_json =&lt;br /&gt;
                c_api_data.data[0];&lt;br /&gt;
            Object.assign(phrases,    c_i18n_json.phrases);&lt;br /&gt;
            Object.assign(dictionary, c_i18n_json.dictionary);&lt;br /&gt;
         },&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 06|Teil 6 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_06&amp;diff=53233</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 06</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_06&amp;diff=53233"/>
		<updated>2025-05-12T15:32:58Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Hello World Vue 06c */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v06&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v06a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v06b&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
Die Version des [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05|fünften Teils des Vue-Tutoriums]]&lt;br /&gt;
wird so angepasst, dass die Sprache zur Laufzeit geändert werden kann. &lt;br /&gt;
&lt;br /&gt;
Zunächst wird jedoch eine Navigationskomponente eingeführt. Dies hat den Zweck, das Routing an einem Beispiel zu erklären.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05d    # Wechsle in den Branch v05d mit Express-Server&lt;br /&gt;
git checkout -b v06  # Klone v05d in einen neuen Branch v06&lt;br /&gt;
rm -rf express_Hello_world&lt;br /&gt;
cd frontend&lt;br /&gt;
npm i&lt;br /&gt;
npm i vue-router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;public/json/config.jso&amp;lt;/code&amp;gt;&lt;br /&gt;
aktivieren wir wieder den Zugriff auf die lokalen JSON-Dateien.&lt;br /&gt;
Den Zugriff auf den Backend-Server, um die Bilder von dort zu holen, behandeln wir später.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fügen Sie außerdem in die Datei &amp;lt;code&amp;gt;vite.config.common.js&amp;lt;/code&amp;gt; zwei geeignete Aliase für &lt;br /&gt;
&amp;lt;code&amp;gt;view&amp;lt;/code&amp;gt; und &amp;lt;/code&amp;gt;router&amp;lt;/code&amp;gt; ein.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Anstelle von Sections werden im Frontend Views verwendet.&lt;br /&gt;
Dazu werden die Section-Komponenten in den Ordner View verschoben und umbenannt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
src/view/ViewHello.vue&lt;br /&gt;
src/view/ViewHome.vue     // an Stelle von ViewForm.vue&lt;br /&gt;
&lt;br /&gt;
src/view/ViewError404.vue // neu&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die CSS-Dateien müssen natürlich auch entsprechend verschoben und abgepasst werden.&lt;br /&gt;
&lt;br /&gt;
ViewForm.vue wird in ViewHome.vue unbenannt,&lt;br /&gt;
da diese Seite als Startseite verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird eine Seite ViewError404.vue angelegt, die immer dann ausgeliefert wird, wenn der Benutzer auf eine URL zugreift, der keine View-Datei zugeordnet ist.&lt;br /&gt;
&lt;br /&gt;
Die CSS-Dateien sollten analog umbenannt und unter den neuen Namen importiert werden. Für &amp;lt;code&amp;gt;ViewError404.vue&amp;lt;/code&amp;gt; muss natürlich auch eine CSS-Datei angelegt werden. Diese verweist allerdings nur auf &amp;lt;code&amp;gt;View.css&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/view/ViewError404.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import router    from &#039;/router&#039;&lt;br /&gt;
  import storeI18n from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const phrases = storeI18n().phrases&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;h1&amp;gt;404&amp;lt;/h1&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;{{phrases.pageNotFound.replace(&#039;$1&#039;, router.currentRoute.value.path)}}&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/view/ViewError404.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Router-Plugin ===&lt;br /&gt;
&lt;br /&gt;
Um mit Vue Multipage-Anwendungen realisieren zu können, benötigt man einRouter-Plugin.&lt;br /&gt;
Dieses muss  zunächst händisch auf Basis von &amp;lt;code&amp;gt;vue-router&amp;lt;/code&amp;gt; implementiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/router/index.js&lt;br /&gt;
&lt;br /&gt;
import { createRouter, createWebHistory } from &#039;vue-router&#039;&lt;br /&gt;
import ViewHome                           from &#039;/view/ViewHome.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  routes =&lt;br /&gt;
    [ { path: &#039;/&#039;,&lt;br /&gt;
        name: &#039;home&#039;,&lt;br /&gt;
        component: ViewHome,&lt;br /&gt;
      },&lt;br /&gt;
      { path: &#039;/hello&#039;,&lt;br /&gt;
        name: &#039;hello&#039;,&lt;br /&gt;
        component: () =&amp;gt; import(&#039;/view/ViewHello.vue&#039;),&lt;br /&gt;
      },&lt;br /&gt;
      { path:      &#039;/:pathMatch(.*)&#039;,&lt;br /&gt;
        component: () =&amp;gt; import(&#039;/view/ViewError404.vue&#039;),&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
&lt;br /&gt;
  router =&lt;br /&gt;
    createRouter({ history: createWebHistory(), routes })&lt;br /&gt;
&lt;br /&gt;
export default router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun muss man das neu erstellte Plugin in die App integrieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import router          from &#039;./router&#039;  // &#039;./router/index.js&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
const&lt;br /&gt;
  pinia   = createPinia(),&lt;br /&gt;
  app     = createApp(App),&lt;br /&gt;
  init    = async () =&amp;gt;&lt;br /&gt;
            { app&lt;br /&gt;
                .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
                .use(pinia)&lt;br /&gt;
                .use(router)&lt;br /&gt;
                .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== index.html ===&lt;br /&gt;
&lt;br /&gt;
Da das &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;-Tag nun innerhalb von &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; verwendet werden soll, &lt;br /&gt;
wird es aus der Datei &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; entfernt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  &amp;lt;body id=&amp;quot;app&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== App.vue ===&lt;br /&gt;
&lt;br /&gt;
Nun kann in die App eine Navigation eingebaut werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- /src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import AppNav       from &#039;/component/AppNav.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeI18n    from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  storeI18n();&lt;br /&gt;
&lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p      v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;AppNav v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;main   v-if=&amp;quot;session.isInitialized&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;router-view/&amp;gt;&lt;br /&gt;
  &amp;lt;/main&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
  @import &#039;/css/body.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zugehörige Navigationskomponente muss auch noch implementiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- /src/component/AppNav.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;nav&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/&amp;quot;&amp;gt;Home&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/hello&amp;quot;&amp;gt;Hello&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
  &amp;lt;/nav&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/AppNav.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* src/css/_config.css */&lt;br /&gt;
...&lt;br /&gt;
--background-color:  #C5EFFC;&lt;br /&gt;
--link-color:        #0027d4;&lt;br /&gt;
...&lt;br /&gt;
--nav-dist:          0.5em;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* src/css/AppNav.css */&lt;br /&gt;
&lt;br /&gt;
@import &#039;config.css&#039;;&lt;br /&gt;
&lt;br /&gt;
nav&lt;br /&gt;
{ top:      0;&lt;br /&gt;
  width:    100%;&lt;br /&gt;
  position: fixed;&lt;br /&gt;
&lt;br /&gt;
  ul&lt;br /&gt;
  { list-style-type: none;&lt;br /&gt;
    margin: 0 var(--nav-dist);&lt;br /&gt;
    padding: 0;&lt;br /&gt;
&lt;br /&gt;
    li&lt;br /&gt;
    { display: inline;&lt;br /&gt;
      a { padding: 0 var(--nav-dist); }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    a&lt;br /&gt;
    { text-decoration: none;&lt;br /&gt;
      color:           var(--link-color);&lt;br /&gt;
      font-weight:     bold;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    a:hover, .router-link-exact-active&lt;br /&gt;
    { text-decoration: underline;&lt;br /&gt;
      color:           var(--link-color);&lt;br /&gt;
      font-weight:     bold;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  label, input&lt;br /&gt;
  { margin: 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Navigation ===&lt;br /&gt;
Die Navigation muss in allen Komponenten, in den Seiten gewechselt werden, angepasst werden.&lt;br /&gt;
&lt;br /&gt;
Das ist in der App.vue der Fall (siehe zuvor) sowie in der ViewHome.vue.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- /src/vie/ViewHome.vue  --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
  import router        from &#039;/router&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    sayHello   = () =&amp;gt; router.push(&#039;/hello&#039;)&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Section-Verwaltung der ehemaligen Single-Page-Anwendung kann und sollte gelöscht werden:&lt;br /&gt;
* &amp;lt;code&amp;gt;config.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;storeSession.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ViewHome.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ViewHello.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; kann (samt CSS-Datei) ganz gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06a ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die Ausgabesprache soll vom Benutzer zur Laufzeit geändert werden können. Die Sprachen sollen durch Bilder (Flaggen) symbolisiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v06      # Wechsle in den Branch v06&lt;br /&gt;
git checkout -b v06a  # Klone v06 in einen neuen Branch v06a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Um die vorhandenen Sprachen durchblättern zu können, wird ein einfacher Automat verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
  &amp;quot;nextLanguage&amp;quot;:    {&amp;quot;de&amp;quot;: &amp;quot;en&amp;quot;, &amp;quot;en&amp;quot;: &amp;quot;de&amp;quot;}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== StoreI18n.js: Integration von nextLanguage ===&lt;br /&gt;
&lt;br /&gt;
Der Automat &amp;lt;code&amp;gt;nextLanguage&amp;lt;/code&amp;gt; wird in den StoreI18n integriert. Wenn die Methode &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; ohne Argument aufgerufen wird, soll der Automat die aktuelle Sprache &amp;lt;code&amp;gt;lang&amp;lt;/code&amp;gt; durch die Nachfolgersprache ersetzen.&lt;br /&gt;
Dazu sollte zunächst die zunächst Konstante &amp;lt;code&amp;gt;nextLanguage&amp;lt;/code&amp;gt; definiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
lang =&lt;br /&gt;
  ref(null),&lt;br /&gt;
&lt;br /&gt;
nextLanguage =&lt;br /&gt;
  reactive(config.nextLanguage),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese neue Konstante &amp;lt;code&amp;gt;nextLanguage&amp;lt;/code&amp;gt; wird verwendet, um die Sprache mittels &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; zu definieren, sofern&lt;br /&gt;
kein Argument übergeben wurde.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; soll bei einer Änderung der Sprache sowohl die notwendige JSON-Sprach-Datei laden als auch die in der HTML-Datei &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; ausgewiesene Sprache anpassen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/storeI18N.js&lt;br /&gt;
&lt;br /&gt;
changeLang =&lt;br /&gt;
  async (p_lang = null) =&amp;gt;&lt;br /&gt;
        { lang.value = p_lang ?? nextLanguage[lang.value];&lt;br /&gt;
          await getI18n(lang.value);&lt;br /&gt;
          document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
initialize = &lt;br /&gt;
  async resolve =&amp;gt; &lt;br /&gt;
        { await changeLang(config.defaultLanguage); &lt;br /&gt;
          resolve(); &lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bilder von Flaggen ===&lt;br /&gt;
&lt;br /&gt;
Zur Visualisierung der gewählten Sprache werden Bilder von Flaggen eingesetzt.&lt;br /&gt;
Wir kopieren die Bilder im SVG-Format aus dem NPM-Paket &amp;quot;https://github.com/lipis/flag-icons&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D flag-icons&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Ordner &amp;quot;node_modules/flag-icons/flags/4x3&amp;quot; finden Sie Flaggen zahlreicher Länder im 4-zu-3-Format.&lt;br /&gt;
Kopieren Sie die Flaggen der Länder Deutschland und Großbritanien in den Ordner &amp;lt;code&amp;gt;public/img&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In die Datei config.js wird der Pfad, unter dem die Bilder zu finden sind, eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
//   public/config.js&lt;br /&gt;
...&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:  &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;imgPath&amp;quot;:  &amp;quot;/img/$1.svg&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;: &amp;quot;/api/$1&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;FormButtonIcon&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Komponente wird benötigt: FormButtonIcon&lt;br /&gt;
Dabei handelt es sich um einen Button, der ein Icon anzeigt.&lt;br /&gt;
Das Bild kann zur Laufzeit geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import { computed, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    props       = defineProps&lt;br /&gt;
                  ({ image: { type: String },&lt;br /&gt;
                     width: { type: String, default: &#039;3em&#039;},&lt;br /&gt;
                  }),&lt;br /&gt;
    config      = inject(&#039;config&#039;),&lt;br /&gt;
    emit        = defineEmits([&#039;click&#039;]),&lt;br /&gt;
    click       = () =&amp;gt; emit(&#039;click&#039;),&lt;br /&gt;
    require     = v_url =&amp;gt; import.meta.url == null&lt;br /&gt;
                           ? v_url&lt;br /&gt;
                           : new URL(v_url, import.meta.url).href,&lt;br /&gt;
    imgPath     = config.imgPath,&lt;br /&gt;
    buttonImage = computed(() =&amp;gt; require(imgPath.replace(&#039;$1&#039;, props.image)))&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;input type=&amp;quot;image&amp;quot; :src=&amp;quot;buttonImage&amp;quot; :style=&amp;quot;{width}&amp;quot; @click=&amp;quot;click&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/form/FormButtonIcon.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
/* src/css/component/FormButtonIcon.css */&lt;br /&gt;
&lt;br /&gt;
@import &#039;FormButton.css&#039;;&lt;br /&gt;
&lt;br /&gt;
input&lt;br /&gt;
{ width:       $button_icon_width !important;&lt;br /&gt;
  margin-left: $nav-dist          !important;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fügen Sie in &amp;lt;code&amp;gt;src/css/_config.css&amp;lt;/code&amp;gt; noch &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
--button-icon-width: 1em;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ein.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterung von &amp;lt;code&amp;gt;AppNav.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Sprachwahl-Button muss noch in die Navigation eingebaut werden. In diesem Rahmen wird auch gleich die Navigation etwas internationalisiert.&lt;br /&gt;
&lt;br /&gt;
Ergänzen Sie in den Dictionaries der Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt;&lt;br /&gt;
folgende Attribute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
    ...         ...,&lt;br /&gt;
    &amp;quot;navHome&amp;quot;:  &amp;quot;Start&amp;quot;,&lt;br /&gt;
    &amp;quot;navHello&amp;quot;: &amp;quot;Hallo&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
    ...         ...,&lt;br /&gt;
    &amp;quot;navHome&amp;quot;:  &amp;quot;Home&amp;quot;,&lt;br /&gt;
    &amp;quot;navHello&amp;quot;: &amp;quot;Hello&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können Sie das Navigationsmenü internationalisieren (einschließlich Auswahl der Sprache per Button).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/component/AppNav.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButtonIcon from &#039;/component/form/FormButtonIcon.vue&#039;&lt;br /&gt;
  import storeI18n      from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const i18n = storeI18n()&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;nav&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/&amp;quot;&amp;gt;{{i18n.dictionary.navHome}}&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/hello&amp;quot;&amp;gt;{{i18n.dictionary.navHello}}&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;FormButtonIcon :image=&amp;quot;i18n.lang&amp;quot; @click=&amp;quot;i18n.changeLang()&amp;quot;/&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
  &amp;lt;/nav&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/AppNav.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06b ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Sorgen Sie dafür, dass &amp;lt;code&amp;gt;autofocus&amp;lt;/code&amp;gt; korrekt funktioniert, insbesondere beim Seitenwechsel via Navigation.&lt;br /&gt;
&lt;br /&gt;
Es ist sinnvoll, die Funktionalität in ein eigenes Store-Modul auszulagern, damit&lt;br /&gt;
das Session-Modul in Anwendungen, die keine Autofocus-Funktionalität benötigen, keinen&lt;br /&gt;
überflüssigen Code enthalten (Bertrand Meyer: Jedes Modul erfüllt genau eine Aufgabe).&lt;br /&gt;
Das Session-Modul wird entsprechend abgespeckt.&lt;br /&gt;
 &lt;br /&gt;
Der Autofocus-Store soll zwei Funktionen bieten:&lt;br /&gt;
* &amp;lt;code&amp;gt;setAutofocusId&amp;lt;/code&amp;gt;: Speichert die ID des Autofokus-Elements der aktuellen Seite (Default: &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;lt;code&amp;gt;setFocus&amp;lt;/code&amp;gt;: Setzt den Focus auf die aktuelle Autofokus-Komponente (sofern die ID ungleich &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; ist). Insbesondere für das Window-Ereignis &amp;lt;code&amp;gt;&#039;visibilitychange&#039;&amp;lt;/code&amp;gt; soll diese Funktion&lt;br /&gt;
aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/StoreAutofocus.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore } from &#039;pinia&#039;&lt;br /&gt;
import { ref }         from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeAutofocus =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;autofocus&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      c_autofocus_id = ref(),&lt;br /&gt;
      setFocus       = () =&amp;gt; document.getElementById(c_autofocus_id.value)?.focus(),&lt;br /&gt;
      setAutofocusId = p_autofocus_id =&amp;gt; { c_autofocus_id.value = p_autofocus_id; &lt;br /&gt;
                                           setFocus();&lt;br /&gt;
                                         }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;visibilitychange&#039;, setFocus);&lt;br /&gt;
&lt;br /&gt;
    return { setAutofocusId, setFocus }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeAutofocus&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore } from &#039;pinia&#039;&lt;br /&gt;
import { ref }         from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die aktuelle ID speichern zu können, muss das zugehörige Textfeld die Information, welche ID es hat, &lt;br /&gt;
per &amp;lt;code&amp;gt;emit&amp;lt;/code&amp;gt; an die Elternkomponente ausgeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
// /src/component/form/FormTextfield.vue&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
const&lt;br /&gt;
  props = defineProps&lt;br /&gt;
          ({ id: { type: String,  default: &#039;&#039; },&lt;br /&gt;
             ...&lt;br /&gt;
          })&lt;br /&gt;
  ... &lt;br /&gt;
  emit  = defineEmits([&#039;update:id&#039;, &#039;update:text&#039;, &#039;enter&#039;]),&lt;br /&gt;
...&lt;br /&gt;
emit(&#039;id&#039;, inputId);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen die Views, die ein Autofokus-Feld beinhalten, dieses an den Session-Store melden, &lt;br /&gt;
sobald sie per Routing aktiviert werden. Das ist in unserem Fall nur die &amp;lt;code&amp;gt;ViewHome&amp;lt;/code&amp;gt;.&lt;br /&gt;
Ein Watcher sorgt dafür, dass die ID des Textfeldes im Session-Objekt gespeichert wird, sobald &lt;br /&gt;
das zugehörige Textfeld die ID per &amp;lt;code&amp;gt;emit&amp;lt;/code&amp;gt; meldet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
// /src/view/ViewHome.vue&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import { ref, watch } from &#039;vue&#039;&lt;br /&gt;
  ...&lt;br /&gt;
  import storeAutofocus from &#039;/store/StoreAutofocus&#039;&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    id         = ref(null),&lt;br /&gt;
    autofocus  = storeAutofocus(),&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
  watch( id, () =&amp;gt; autofocus.setAutofocusId(id.value) )&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
        &amp;lt;FormTextfield @id=&amp;quot;setId&amp;quot;&lt;br /&gt;
          ...&lt;br /&gt;
        &amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schluss muss im Router noch die Autofocus-ID bei jedem Seitenwechsel gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/router/index.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import storeAutofocus from &#039;/store/StoreAutofocus&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
router.beforeEach(() =&amp;gt; storeAutofocus().setAutofocusId())&lt;br /&gt;
&lt;br /&gt;
export default router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist hier, dass man keine Konstante &amp;lt;code&amp;gt;session&amp;lt;/code&amp;gt; definieren kann, da die Stores erst nach dem Router-Objekt erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/router/index.js&lt;br /&gt;
...&lt;br /&gt;
import storeAutofocus from &#039;/store/StoreAutofocus&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  autofocus = storeAutofocus(), // FUNKTIONIERT NICHT&lt;br /&gt;
  ...&lt;br /&gt;
router.beforeEach(() =&amp;gt; autofocus.setAutofocusId())&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Funktionsaufruf &amp;lt;code&amp;gt;storeSession()&amp;lt;/code&amp;gt; darf daher erst in  der Callback-Funktion von &lt;br /&gt;
&amp;lt;code&amp;gt;router.beforeEach&amp;lt;/code&amp;gt; aufgerufen werden. Zu diesem Zeitpunkt sind die Stores und der Router auf jeden Fall schon initialisiert worden.&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss sollten Sie noch versuchen, &amp;lt;code&amp;gt;/src/component/AppNav.vue&amp;lt;/code&amp;gt; so abzuändern,&lt;br /&gt;
dass ein Klick auf die Fahne nicht nur die Sprache ändert, sonden auch noch den Focus auf das Startfeld setzt (such wenn dieses Feature vermutlich eher kontraproduktiv ist).&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06c ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Sorgen Sie dafür, dass die unterstützten Sprachen und der Sprach-Automat vom Backend&lt;br /&gt;
geladen wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v06b     # Wechsle in den Branch v06b&lt;br /&gt;
git checkout -b v06c  # Klone v06b in einen neuen Branch v06c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Splitten Sie die Config-Datei zunächst in zwei Teile: &amp;quot;config.json&amp;quot; wird aus dem Public-Ordner geladen.&lt;br /&gt;
&amp;quot;config_backend.json&amp;quot; wird normalerweise vom Backend geladen, kann aber zum Entwicklungszeitpunkt auch vom Public-Ordner geladen werden, wenn das Backend noch nicht stabil läuft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;configBackend&amp;quot;:  &amp;quot;/json/config_backend.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;imgPath&amp;quot;:        &amp;quot;/img/$1.svg&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:       &amp;quot;/api/$1&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/public/json/config_backend.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;languages&amp;quot;:    [&amp;quot;de&amp;quot;, &amp;quot;en&amp;quot;],&lt;br /&gt;
  &amp;quot;nextLanguage&amp;quot;: {&amp;quot;de&amp;quot;: &amp;quot;en&amp;quot;, &amp;quot;en&amp;quot;: &amp;quot;de&amp;quot;}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Backend teilt dem Frontend mit, welche Sprachen unterstützt werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Laden Sie, nachdem Sie &amp;quot;config&amp;quot; asynchron geladen haben, &amp;quot;configBackend&amp;quot; asynchron und kopieren Sie den Inhalt dieses Objekts in das Config-Objekt. Den Pfad von &amp;quot;configBackend&amp;quot; finden Sie unter &amp;quot;config.configBackend&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
const&lt;br /&gt;
  ...&lt;br /&gt;
  init = async () =&amp;gt;&lt;br /&gt;
         { const&lt;br /&gt;
             config        = await getJson(&#039;/json/config.json&#039;),&lt;br /&gt;
             configBackend = await getJson(config.configBackend)&lt;br /&gt;
&lt;br /&gt;
           Object.assign(config, configBackend)&lt;br /&gt;
&lt;br /&gt;
           app&lt;br /&gt;
             .provide(&#039;config&#039;, config)&lt;br /&gt;
             .use(pinia)&lt;br /&gt;
             .use(router)&lt;br /&gt;
             .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
         }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt es im Konfigurationsobjekt keine Defaultsprache mehr.&lt;br /&gt;
Im I18N-Store wird daher ab sofort die erste Sprache in der Liste der&lt;br /&gt;
unterstützten Sprachen als Defaultsprache genutzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/storeI18N.js&lt;br /&gt;
&lt;br /&gt;
initialize = &lt;br /&gt;
  async resolve =&amp;gt; &lt;br /&gt;
        { await changeLang(config.languages[0]); &lt;br /&gt;
          resolve(); &lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06d ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Defaultmäßig soll dem Benutzer die App in der Sprache angezeigt werden, die der Benutzer&lt;br /&gt;
im Browser voreingestellt hat. Sollte die voreingestellte Sprache nicht unterstützt werden, wird die App in der Defaultsprache angezeigt.&lt;br /&gt;
&lt;br /&gt;
Legen Sie zunächst wieder einen neuen Branch an.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v06c     # Wechsle in den Branch v06c&lt;br /&gt;
git checkout -b v06d  # Klone v06c in einen neuen Branch v06d&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Vorgehen ist im Prinzip ganz einfach.&lt;br /&gt;
&lt;br /&gt;
Mittels des NPM-Packets &amp;quot;navigator-languages&amp;quot; ermittelt man die vom Browser unterstützten&lt;br /&gt;
Sprachen.  In der Konfigurationsdatei listet man die Sprachen auf, die von der App unterstützt werden.&lt;br /&gt;
Besser wäre es natürlich, diese Liste vom Backend zu laden, damit die App mit bekommt, wenn neue&lt;br /&gt;
Sprachen zur Verfügung stehen. Am aufwändigsten ist es, diese beiden Array zu matchen.&lt;br /&gt;
&lt;br /&gt;
Mögliche Erweiterung: Auch die HTML-Pfade sollten internationalisiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm i navigator-languages // unterstützt mehrere Browservarianten&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Language Matching ===&lt;br /&gt;
&lt;br /&gt;
Das Language Matching erfolgt zweimal mit zwei verschachtelten Schleifen.&lt;br /&gt;
Zunächst wird überprüft, ob es exakte Übereinstimmungen gibt. Sollte das nicht der Fall sein, wird überprüft, ob es Matches zwischen den ersten beiden Buchstaben gibt (&#039;de&#039;, &#039;de-AT&#039;, &#039;de-DE&#039; matchen in diesem Schritt alle miteinander).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/util/chooseLanguage.js&lt;br /&gt;
&lt;br /&gt;
import navigatorLanguages from &#039;navigator-languages&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  /**&lt;br /&gt;
   * @function&lt;br /&gt;
   * @param  { Array&amp;lt;string&amp;gt; } p_langs_available &lt;br /&gt;
   *         An array of language strings containing the languages&lt;br /&gt;
   *         supported by the app &lt;br /&gt;
   * @param  { Array&amp;lt;string&amp;gt; } [p_langs_required  = navigatorLanguages()]&lt;br /&gt;
   *         An array of language strings containing the languages&lt;br /&gt;
   *         requested by the client &lt;br /&gt;
   * @return { string } &lt;br /&gt;
   *         The language to be displayed&lt;br /&gt;
   */&lt;br /&gt;
  chooseLanguage =&lt;br /&gt;
  (p_langs_available, p_langs_required = navigatorLanguages()) =&amp;gt;&lt;br /&gt;
  { let&lt;br /&gt;
      lang = p_langs_available[0],&lt;br /&gt;
      i=0, j=0, n=p_langs_required.length, m=p_langs_available.length&lt;br /&gt;
  &lt;br /&gt;
    outer:&lt;br /&gt;
    { while (i&amp;lt;n)&lt;br /&gt;
      { const c_lang_required = p_langs_required[i]&lt;br /&gt;
        while (j&amp;lt;m)&lt;br /&gt;
        { const c_lang_availabe = p_langs_available[j]&lt;br /&gt;
          if (c_lang_availabe === c_lang_required)&lt;br /&gt;
          // both languages match perfectly&lt;br /&gt;
          { lang = c_lang_availabe;&lt;br /&gt;
            break outer;&lt;br /&gt;
          }&lt;br /&gt;
          j++&lt;br /&gt;
        }&lt;br /&gt;
        i++&lt;br /&gt;
      }&lt;br /&gt;
      i=0, j=0&lt;br /&gt;
      while (i&amp;lt;n)&lt;br /&gt;
      { const c_lang_required = p_langs_required[i]&lt;br /&gt;
        while (j&amp;lt;m)&lt;br /&gt;
        { const c_lang_availabe = p_langs_available[j]&lt;br /&gt;
          if (c_lang_availabe.substring(0,2) === c_lang_required.substring(0,2))&lt;br /&gt;
          // main part of the languages match&lt;br /&gt;
          { lang = c_lang_availabe;&lt;br /&gt;
            break outer;&lt;br /&gt;
          }&lt;br /&gt;
          j++&lt;br /&gt;
        }&lt;br /&gt;
        i++&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  &lt;br /&gt;
     return lang&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
export default chooseLanguage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/store/StoreI18n&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Im I18N-Store wird nun beim Initialisieren nicht nicht mehr eine Defaultspache geladen, sondern &lt;br /&gt;
die Sprache, die möglichst nah an den Wünschen des im Browser des Benutzers gespeicherten Sprachwünschen liegt.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass Sie in die Datei &amp;lt;code&amp;gt;vite.config.common.js&amp;lt;/code&amp;gt; einen geeigneten Alias für den &lt;br /&gt;
&amp;lt;code&amp;gt;util&amp;lt;/code&amp;gt;-Pfad definieren müssen, um &amp;lt;code&amp;gt;&#039;/util/chooseLanguage&#039;&amp;lt;/code&amp;gt; an Stelle von&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/util/chooseLanguage&#039;&amp;lt;/code&amp;gt; schreiben zu können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
import chooseLanguage from &#039;/util/chooseLanguage&#039;&lt;br /&gt;
...&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          ...&lt;br /&gt;
          initialize = &lt;br /&gt;
             async resolve =&amp;gt; &lt;br /&gt;
                   { await changeLang(chooseLanguage(config.languages)); &lt;br /&gt;
                     resolve(); &lt;br /&gt;
                   }&lt;br /&gt;
        ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 07|Teil 7 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_06&amp;diff=53232</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 06</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_06&amp;diff=53232"/>
		<updated>2025-05-12T15:32:26Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Bilder von Flaggen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Musterlösung&#039;&#039;&#039;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v06&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v06a&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;... v06b&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
Die Version des [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_05|fünften Teils des Vue-Tutoriums]]&lt;br /&gt;
wird so angepasst, dass die Sprache zur Laufzeit geändert werden kann. &lt;br /&gt;
&lt;br /&gt;
Zunächst wird jedoch eine Navigationskomponente eingeführt. Dies hat den Zweck, das Routing an einem Beispiel zu erklären.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Konfigurieren und internationalisieren Sie die Anwendung mit Hilfe von dynamischen JSON-Dateien.&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt; und fügen Sie das Package &amp;lt;code&amp;gt;uuid&amp;lt;/code&amp;gt; hinzu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v05d    # Wechsle in den Branch v05d mit Express-Server&lt;br /&gt;
git checkout -b v06  # Klone v05d in einen neuen Branch v06&lt;br /&gt;
rm -rf express_Hello_world&lt;br /&gt;
cd frontend&lt;br /&gt;
npm i&lt;br /&gt;
npm i vue-router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;public/json/config.jso&amp;lt;/code&amp;gt;&lt;br /&gt;
aktivieren wir wieder den Zugriff auf die lokalen JSON-Dateien.&lt;br /&gt;
Den Zugriff auf den Backend-Server, um die Bilder von dort zu holen, behandeln wir später.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{ &amp;quot;startSection&amp;quot;:    &amp;quot;form&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;de&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fügen Sie außerdem in die Datei &amp;lt;code&amp;gt;vite.config.common.js&amp;lt;/code&amp;gt; zwei geeignete Aliase für &lt;br /&gt;
&amp;lt;code&amp;gt;view&amp;lt;/code&amp;gt; und &amp;lt;/code&amp;gt;router&amp;lt;/code&amp;gt; ein.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Anstelle von Sections werden im Frontend Views verwendet.&lt;br /&gt;
Dazu werden die Section-Komponenten in den Ordner View verschoben und umbenannt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
src/view/ViewHello.vue&lt;br /&gt;
src/view/ViewHome.vue     // an Stelle von ViewForm.vue&lt;br /&gt;
&lt;br /&gt;
src/view/ViewError404.vue // neu&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die CSS-Dateien müssen natürlich auch entsprechend verschoben und abgepasst werden.&lt;br /&gt;
&lt;br /&gt;
ViewForm.vue wird in ViewHome.vue unbenannt,&lt;br /&gt;
da diese Seite als Startseite verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird eine Seite ViewError404.vue angelegt, die immer dann ausgeliefert wird, wenn der Benutzer auf eine URL zugreift, der keine View-Datei zugeordnet ist.&lt;br /&gt;
&lt;br /&gt;
Die CSS-Dateien sollten analog umbenannt und unter den neuen Namen importiert werden. Für &amp;lt;code&amp;gt;ViewError404.vue&amp;lt;/code&amp;gt; muss natürlich auch eine CSS-Datei angelegt werden. Diese verweist allerdings nur auf &amp;lt;code&amp;gt;View.css&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/view/ViewError404.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import router    from &#039;/router&#039;&lt;br /&gt;
  import storeI18n from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const phrases = storeI18n().phrases&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;h1&amp;gt;404&amp;lt;/h1&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;{{phrases.pageNotFound.replace(&#039;$1&#039;, router.currentRoute.value.path)}}&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/view/ViewError404.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Router-Plugin ===&lt;br /&gt;
&lt;br /&gt;
Um mit Vue Multipage-Anwendungen realisieren zu können, benötigt man einRouter-Plugin.&lt;br /&gt;
Dieses muss  zunächst händisch auf Basis von &amp;lt;code&amp;gt;vue-router&amp;lt;/code&amp;gt; implementiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/router/index.js&lt;br /&gt;
&lt;br /&gt;
import { createRouter, createWebHistory } from &#039;vue-router&#039;&lt;br /&gt;
import ViewHome                           from &#039;/view/ViewHome.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  routes =&lt;br /&gt;
    [ { path: &#039;/&#039;,&lt;br /&gt;
        name: &#039;home&#039;,&lt;br /&gt;
        component: ViewHome,&lt;br /&gt;
      },&lt;br /&gt;
      { path: &#039;/hello&#039;,&lt;br /&gt;
        name: &#039;hello&#039;,&lt;br /&gt;
        component: () =&amp;gt; import(&#039;/view/ViewHello.vue&#039;),&lt;br /&gt;
      },&lt;br /&gt;
      { path:      &#039;/:pathMatch(.*)&#039;,&lt;br /&gt;
        component: () =&amp;gt; import(&#039;/view/ViewError404.vue&#039;),&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
&lt;br /&gt;
  router =&lt;br /&gt;
    createRouter({ history: createWebHistory(), routes })&lt;br /&gt;
&lt;br /&gt;
export default router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun muss man das neu erstellte Plugin in die App integrieren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import getJson         from &#039;/service/getJson&#039;&lt;br /&gt;
import router          from &#039;./router&#039;  // &#039;./router/index.js&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
const&lt;br /&gt;
  pinia   = createPinia(),&lt;br /&gt;
  app     = createApp(App),&lt;br /&gt;
  init    = async () =&amp;gt;&lt;br /&gt;
            { app&lt;br /&gt;
                .provide(&#039;config&#039;, await getJson(&#039;/json/config.json&#039;))&lt;br /&gt;
                .use(pinia)&lt;br /&gt;
                .use(router)&lt;br /&gt;
                .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
window.addEventListener(&#039;load&#039;, init)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== index.html ===&lt;br /&gt;
&lt;br /&gt;
Da das &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;-Tag nun innerhalb von &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt; verwendet werden soll, &lt;br /&gt;
wird es aus der Datei &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; entfernt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
  &amp;lt;body id=&amp;quot;app&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;/body&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== App.vue ===&lt;br /&gt;
&lt;br /&gt;
Nun kann in die App eine Navigation eingebaut werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- /src/App.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import AppNav       from &#039;/component/AppNav.vue&#039;&lt;br /&gt;
  import storeSession from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeI18n    from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const session = storeSession()&lt;br /&gt;
&lt;br /&gt;
  storeI18n();&lt;br /&gt;
&lt;br /&gt;
  session.initialize();&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;p      v-if=&amp;quot;!session.isInitialized&amp;quot;&amp;gt;Loading ...&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;AppNav v-if=&amp;quot;session.isInitialized&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;main   v-if=&amp;quot;session.isInitialized&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;router-view/&amp;gt;&lt;br /&gt;
  &amp;lt;/main&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style&amp;gt;&lt;br /&gt;
  @import &#039;/css/body.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zugehörige Navigationskomponente muss auch noch implementiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- /src/component/AppNav.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;nav&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/&amp;quot;&amp;gt;Home&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/hello&amp;quot;&amp;gt;Hello&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
  &amp;lt;/nav&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/AppNav.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
/* src/css/_config.css */&lt;br /&gt;
...&lt;br /&gt;
--background-color:  #C5EFFC;&lt;br /&gt;
--link-color:        #0027d4;&lt;br /&gt;
...&lt;br /&gt;
--nav-dist:          0.5em;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* src/css/AppNav.css */&lt;br /&gt;
&lt;br /&gt;
@import &#039;config.css&#039;;&lt;br /&gt;
&lt;br /&gt;
nav&lt;br /&gt;
{ top:      0;&lt;br /&gt;
  width:    100%;&lt;br /&gt;
  position: fixed;&lt;br /&gt;
&lt;br /&gt;
  ul&lt;br /&gt;
  { list-style-type: none;&lt;br /&gt;
    margin: 0 var(--nav-dist);&lt;br /&gt;
    padding: 0;&lt;br /&gt;
&lt;br /&gt;
    li&lt;br /&gt;
    { display: inline;&lt;br /&gt;
      a { padding: 0 var(--nav-dist); }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    a&lt;br /&gt;
    { text-decoration: none;&lt;br /&gt;
      color:           var(--link-color);&lt;br /&gt;
      font-weight:     bold;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    a:hover, .router-link-exact-active&lt;br /&gt;
    { text-decoration: underline;&lt;br /&gt;
      color:           var(--link-color);&lt;br /&gt;
      font-weight:     bold;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  label, input&lt;br /&gt;
  { margin: 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Navigation ===&lt;br /&gt;
Die Navigation muss in allen Komponenten, in den Seiten gewechselt werden, angepasst werden.&lt;br /&gt;
&lt;br /&gt;
Das ist in der App.vue der Fall (siehe zuvor) sowie in der ViewHome.vue.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- /src/vie/ViewHome.vue  --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButton    from &#039;/component/form/FormButton.vue&#039;&lt;br /&gt;
  import FormTextfield from &#039;/component/form/FormTextfield.vue&#039;&lt;br /&gt;
&lt;br /&gt;
  import storeSession  from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
  import router        from &#039;/router&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    session    = storeSession(),&lt;br /&gt;
    greeting   = storeGreeting(),&lt;br /&gt;
    dictionary = greeting.dictionary,&lt;br /&gt;
    sayHello   = () =&amp;gt; router.push(&#039;/hello&#039;)&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Section-Verwaltung der ehemaligen Single-Page-Anwendung kann und sollte gelöscht werden:&lt;br /&gt;
* &amp;lt;code&amp;gt;config.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;storeSession.js&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ViewHome.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ViewHello.vue&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;HelloWorld.vue&amp;lt;/code&amp;gt; kann (samt CSS-Datei) ganz gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06a ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Die Ausgabesprache soll vom Benutzer zur Laufzeit geändert werden können. Die Sprachen sollen durch Bilder (Flaggen) symbolisiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v06      # Wechsle in den Branch v06&lt;br /&gt;
git checkout -b v06a  # Klone v06 in einen neuen Branch v06a&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;config.json&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Um die vorhandenen Sprachen durchblättern zu können, wird ein einfacher Automat verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;apiRoot&amp;quot;:         &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:        &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;defaultLanguage&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
  &amp;quot;nextLanguage&amp;quot;:    {&amp;quot;de&amp;quot;: &amp;quot;en&amp;quot;, &amp;quot;en&amp;quot;: &amp;quot;de&amp;quot;}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== StoreI18n.js: Integration von nextLanguage ===&lt;br /&gt;
&lt;br /&gt;
Der Automat &amp;lt;code&amp;gt;nextLanguage&amp;lt;/code&amp;gt; wird in den StoreI18n integriert. Wenn die Methode &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; ohne Argument aufgerufen wird, soll der Automat die aktuelle Sprache &amp;lt;code&amp;gt;lang&amp;lt;/code&amp;gt; durch die Nachfolgersprache ersetzen.&lt;br /&gt;
Dazu sollte zunächst die zunächst Konstante &amp;lt;code&amp;gt;nextLanguage&amp;lt;/code&amp;gt; definiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
lang =&lt;br /&gt;
  ref(null),&lt;br /&gt;
&lt;br /&gt;
nextLanguage =&lt;br /&gt;
  reactive(config.nextLanguage),&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese neue Konstante &amp;lt;code&amp;gt;nextLanguage&amp;lt;/code&amp;gt; wird verwendet, um die Sprache mittels &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; zu definieren, sofern&lt;br /&gt;
kein Argument übergeben wurde.&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;changeLang&amp;lt;/code&amp;gt; soll bei einer Änderung der Sprache sowohl die notwendige JSON-Sprach-Datei laden als auch die in der HTML-Datei &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt; ausgewiesene Sprache anpassen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/storeI18N.js&lt;br /&gt;
&lt;br /&gt;
changeLang =&lt;br /&gt;
  async (p_lang = null) =&amp;gt;&lt;br /&gt;
        { lang.value = p_lang ?? nextLanguage[lang.value];&lt;br /&gt;
          await getI18n(lang.value);&lt;br /&gt;
          document.getElementsByTagName(&#039;html&#039;)[0].lang = lang.value;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
initialize = &lt;br /&gt;
  async resolve =&amp;gt; &lt;br /&gt;
        { await changeLang(config.defaultLanguage); &lt;br /&gt;
          resolve(); &lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bilder von Flaggen ===&lt;br /&gt;
&lt;br /&gt;
Zur Visualisierung der gewählten Sprache werden Bilder von Flaggen eingesetzt.&lt;br /&gt;
Wir kopieren die Bilder im SVG-Format aus dem NPM-Paket &amp;quot;https://github.com/lipis/flag-icons&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm i -D flag-icons&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Ordner &amp;quot;node_modules/flag-icons/flags/4x3&amp;quot; finden Sie Flaggen zahlreicher Länder im 4-zu-3-Format.&lt;br /&gt;
Kopieren Sie die Flaggen der Länder Deutschland und Großbritanien in den Ordner &amp;lt;code&amp;gt;public/img&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In die Datei config.js wird der Pfad, unter dem die Bilder zu finden sind, eingefügt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
//   public/config.js&lt;br /&gt;
...&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:  &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;imgPath&amp;quot;:  &amp;quot;/img/$1.svg&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;: &amp;quot;/api/$1&amp;quot;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;FormButtonIcon&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Komponente wird benötigt: FormButtonIcon&lt;br /&gt;
Dabei handelt es sich um einen Button, der ein Icon anzeigt.&lt;br /&gt;
Das Bild kann zur Laufzeit geändert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import { computed, inject } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    props       = defineProps&lt;br /&gt;
                  ({ image: { type: String },&lt;br /&gt;
                     width: { type: String, default: &#039;3em&#039;},&lt;br /&gt;
                  }),&lt;br /&gt;
    config      = inject(&#039;config&#039;),&lt;br /&gt;
    emit        = defineEmits([&#039;click&#039;]),&lt;br /&gt;
    click       = () =&amp;gt; emit(&#039;click&#039;),&lt;br /&gt;
    require     = v_url =&amp;gt; import.meta.url == null&lt;br /&gt;
                           ? v_url&lt;br /&gt;
                           : new URL(v_url, import.meta.url).href,&lt;br /&gt;
    imgPath     = config.imgPath,&lt;br /&gt;
    buttonImage = computed(() =&amp;gt; require(imgPath.replace(&#039;$1&#039;, props.image)))&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;input type=&amp;quot;image&amp;quot; :src=&amp;quot;buttonImage&amp;quot; :style=&amp;quot;{width}&amp;quot; @click=&amp;quot;click&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/form/FormButtonIcon.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
/* src/css/component/FormButtonIcon.css */&lt;br /&gt;
&lt;br /&gt;
@import &#039;FormButton.css&#039;;&lt;br /&gt;
&lt;br /&gt;
input&lt;br /&gt;
{ width:       $button_icon_width !important;&lt;br /&gt;
  margin-left: $nav-dist          !important;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fügen Sie in &amp;lt;code&amp;gt;src/css/_config.css&amp;lt;/code&amp;gt; noch &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
--button-icon-width: 1em;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ein.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterung von &amp;lt;code&amp;gt;AppNav.vue&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Der Sprachwahl-Button muss noch in die Navigation eingebaut werden. In diesem Rahmen wird auch gleich die Navigation etwas internationalisiert.&lt;br /&gt;
&lt;br /&gt;
Ergänzen Sie in den Dictionaries der Dateien &amp;lt;code&amp;gt;public/json/i18n_de.json&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;public/json/i18n_en.json&amp;lt;/code&amp;gt;&lt;br /&gt;
folgende Attribute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
    ...         ...,&lt;br /&gt;
    &amp;quot;navHome&amp;quot;:  &amp;quot;Start&amp;quot;,&lt;br /&gt;
    &amp;quot;navHello&amp;quot;: &amp;quot;Hallo&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
    ...         ...,&lt;br /&gt;
    &amp;quot;navHome&amp;quot;:  &amp;quot;Home&amp;quot;,&lt;br /&gt;
    &amp;quot;navHello&amp;quot;: &amp;quot;Hello&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können Sie das Navigationsmenü internationalisieren (einschließlich Auswahl der Sprache per Button).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/component/AppNav.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import FormButtonIcon from &#039;/component/form/FormButtonIcon.vue&#039;&lt;br /&gt;
  import storeI18n      from &#039;/store/StoreI18n&#039;&lt;br /&gt;
&lt;br /&gt;
  const i18n = storeI18n()&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;nav&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/&amp;quot;&amp;gt;{{i18n.dictionary.navHome}}&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;router-link to=&amp;quot;/hello&amp;quot;&amp;gt;{{i18n.dictionary.navHello}}&amp;lt;/router-link&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
      &amp;lt;li&amp;gt;&amp;lt;FormButtonIcon :image=&amp;quot;i18n.lang&amp;quot; @click=&amp;quot;i18n.changeLang()&amp;quot;/&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
  &amp;lt;/nav&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;style scoped&amp;gt;&lt;br /&gt;
  @import &#039;/css/component/AppNav.css&#039;;&lt;br /&gt;
&amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06b ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Sorgen Sie dafür, dass &amp;lt;code&amp;gt;autofocus&amp;lt;/code&amp;gt; korrekt funktioniert, insbesondere beim Seitenwechsel via Navigation.&lt;br /&gt;
&lt;br /&gt;
Es ist sinnvoll, die Funktionalität in ein eigenes Store-Modul auszulagern, damit&lt;br /&gt;
das Session-Modul in Anwendungen, die keine Autofocus-Funktionalität benötigen, keinen&lt;br /&gt;
überflüssigen Code enthalten (Bertrand Meyer: Jedes Modul erfüllt genau eine Aufgabe).&lt;br /&gt;
Das Session-Modul wird entsprechend abgespeckt.&lt;br /&gt;
 &lt;br /&gt;
Der Autofocus-Store soll zwei Funktionen bieten:&lt;br /&gt;
* &amp;lt;code&amp;gt;setAutofocusId&amp;lt;/code&amp;gt;: Speichert die ID des Autofokus-Elements der aktuellen Seite (Default: &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;lt;code&amp;gt;setFocus&amp;lt;/code&amp;gt;: Setzt den Focus auf die aktuelle Autofokus-Komponente (sofern die ID ungleich &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; ist). Insbesondere für das Window-Ereignis &amp;lt;code&amp;gt;&#039;visibilitychange&#039;&amp;lt;/code&amp;gt; soll diese Funktion&lt;br /&gt;
aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/StoreAutofocus.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore } from &#039;pinia&#039;&lt;br /&gt;
import { ref }         from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeAutofocus =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;autofocus&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      c_autofocus_id = ref(),&lt;br /&gt;
      setFocus       = () =&amp;gt; document.getElementById(c_autofocus_id.value)?.focus(),&lt;br /&gt;
      setAutofocusId = p_autofocus_id =&amp;gt; { c_autofocus_id.value = p_autofocus_id; &lt;br /&gt;
                                           setFocus();&lt;br /&gt;
                                         }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener(&#039;visibilitychange&#039;, setFocus);&lt;br /&gt;
&lt;br /&gt;
    return { setAutofocusId, setFocus }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeAutofocus&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore } from &#039;pinia&#039;&lt;br /&gt;
import { ref }         from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      initializers   = [],&lt;br /&gt;
      isInitialized  = ref(false),&lt;br /&gt;
      addInitializer = p_initializer =&amp;gt;&lt;br /&gt;
                       initializers.push(new Promise(resolve =&amp;gt; p_initializer(resolve))),&lt;br /&gt;
      initialize     = () =&amp;gt; Promise.all(initializers)&lt;br /&gt;
                                    .then(() =&amp;gt; isInitialized.value = true)&lt;br /&gt;
&lt;br /&gt;
    return { addInitializer, initialize, isInitialized }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die aktuelle ID speichern zu können, muss das zugehörige Textfeld die Information, welche ID es hat, &lt;br /&gt;
per &amp;lt;code&amp;gt;emit&amp;lt;/code&amp;gt; an die Elternkomponente ausgeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
// /src/component/form/FormTextfield.vue&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
const&lt;br /&gt;
  props = defineProps&lt;br /&gt;
          ({ id: { type: String,  default: &#039;&#039; },&lt;br /&gt;
             ...&lt;br /&gt;
          })&lt;br /&gt;
  ... &lt;br /&gt;
  emit  = defineEmits([&#039;update:id&#039;, &#039;update:text&#039;, &#039;enter&#039;]),&lt;br /&gt;
...&lt;br /&gt;
emit(&#039;id&#039;, inputId);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müssen die Views, die ein Autofokus-Feld beinhalten, dieses an den Session-Store melden, &lt;br /&gt;
sobald sie per Routing aktiviert werden. Das ist in unserem Fall nur die &amp;lt;code&amp;gt;ViewHome&amp;lt;/code&amp;gt;.&lt;br /&gt;
Ein Watcher sorgt dafür, dass die ID des Textfeldes im Session-Objekt gespeichert wird, sobald &lt;br /&gt;
das zugehörige Textfeld die ID per &amp;lt;code&amp;gt;emit&amp;lt;/code&amp;gt; meldet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
// /src/view/ViewHome.vue&lt;br /&gt;
&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import { ref, watch } from &#039;vue&#039;&lt;br /&gt;
  ...&lt;br /&gt;
  import storeAutofocus from &#039;/store/StoreAutofocus&#039;&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    id         = ref(null),&lt;br /&gt;
    autofocus  = storeAutofocus(),&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
  watch( id, () =&amp;gt; autofocus.setAutofocusId(id.value) )&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
        &amp;lt;FormTextfield @id=&amp;quot;setId&amp;quot;&lt;br /&gt;
          ...&lt;br /&gt;
        &amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schluss muss im Router noch die Autofocus-ID bei jedem Seitenwechsel gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/router/index.js&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
import storeAutofocus from &#039;/store/StoreAutofocus&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
router.beforeEach(() =&amp;gt; storeAutofocus().setAutofocusId())&lt;br /&gt;
&lt;br /&gt;
export default router&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist hier, dass man keine Konstante &amp;lt;code&amp;gt;session&amp;lt;/code&amp;gt; definieren kann, da die Stores erst nach dem Router-Objekt erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/router/index.js&lt;br /&gt;
...&lt;br /&gt;
import storeAutofocus from &#039;/store/StoreAutofocus&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  autofocus = storeAutofocus(), // FUNKTIONIERT NICHT&lt;br /&gt;
  ...&lt;br /&gt;
router.beforeEach(() =&amp;gt; autofocus.setAutofocusId())&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Funktionsaufruf &amp;lt;code&amp;gt;storeSession()&amp;lt;/code&amp;gt; darf daher erst in  der Callback-Funktion von &lt;br /&gt;
&amp;lt;code&amp;gt;router.beforeEach&amp;lt;/code&amp;gt; aufgerufen werden. Zu diesem Zeitpunkt sind die Stores und der Router auf jeden Fall schon initialisiert worden.&lt;br /&gt;
&lt;br /&gt;
Zum Abschluss sollten Sie noch versuchen, &amp;lt;code&amp;gt;/src/component/AppNav.vue&amp;lt;/code&amp;gt; so abzuändern,&lt;br /&gt;
dass ein Klick auf die Fahne nicht nur die Sprache ändert, sonden auch noch den Focus auf das Startfeld setzt (such wenn dieses Feature vermutlich eher kontraproduktiv ist).&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06c ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Sorgen Sie dafür, dass die unterstützten Sprachen und der Sprach-Automat vom Backend&lt;br /&gt;
geladen wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v06b     # Wechsle in den Branch v06b&lt;br /&gt;
git checkout -b v06c  # Klone v06b in einen neuen Branch v06c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Splitten Sie die Config-Datei zunächst in zwei Teile: &amp;quot;config.json&amp;quot; wird aus dem Public-Ordner geladen.&lt;br /&gt;
&amp;quot;config_backend.json&amp;quot; wird normalerweise vom Backend geladen, kann aber zum Entwicklungszeitpunkt auch vom Public-Ordner geladen werden, wenn das Backend noch nicht stabil läuft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/public/json/config.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;configBackend&amp;quot;:  &amp;quot;/json/config_backend.json&amp;quot;,&lt;br /&gt;
  &amp;quot;apiRoot&amp;quot;:        &amp;quot;/json/i18n_$1.json&amp;quot;,&lt;br /&gt;
  &amp;quot;imgPath&amp;quot;:        &amp;quot;/img/$1.svg&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;XconfigBackend&amp;quot;: &amp;quot;/api/config&amp;quot;,&lt;br /&gt;
  &amp;quot;XapiRoot&amp;quot;:       &amp;quot;/api/$1&amp;quot;,&lt;br /&gt;
  &amp;quot;XimgPath&amp;quot;:       &amp;quot;/api/$1/img&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
// src/public/json/config_backend.json&lt;br /&gt;
&lt;br /&gt;
{ &amp;quot;languages&amp;quot;:    [&amp;quot;de&amp;quot;, &amp;quot;en&amp;quot;],&lt;br /&gt;
  &amp;quot;nextLanguage&amp;quot;: {&amp;quot;de&amp;quot;: &amp;quot;en&amp;quot;, &amp;quot;en&amp;quot;: &amp;quot;de&amp;quot;}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Backend teilt dem Frontend mit, welche Sprachen unterstützt werden.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;main.js&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Laden Sie, nachdem Sie &amp;quot;config&amp;quot; asynchron geladen haben, &amp;quot;configBackend&amp;quot; asynchron und kopieren Sie den Inhalt dieses Objekts in das Config-Objekt. Den Pfad von &amp;quot;configBackend&amp;quot; finden Sie unter &amp;quot;config.configBackend&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
const&lt;br /&gt;
  ...&lt;br /&gt;
  init = async () =&amp;gt;&lt;br /&gt;
         { const&lt;br /&gt;
             config        = await getJson(&#039;/json/config.json&#039;),&lt;br /&gt;
             configBackend = await getJson(config.configBackend)&lt;br /&gt;
&lt;br /&gt;
           Object.assign(config, configBackend)&lt;br /&gt;
&lt;br /&gt;
           app&lt;br /&gt;
             .provide(&#039;config&#039;, config)&lt;br /&gt;
             .use(pinia)&lt;br /&gt;
             .use(router)&lt;br /&gt;
             .mount(&#039;#app&#039;) // app is shown to the user&lt;br /&gt;
         }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gibt es im Konfigurationsobjekt keine Defaultsprache mehr.&lt;br /&gt;
Im I18N-Store wird daher ab sofort die erste Sprache in der Liste der&lt;br /&gt;
unterstützten Sprachen als Defaultsprache genutzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// /src/store/storeI18N.js&lt;br /&gt;
&lt;br /&gt;
initialize = &lt;br /&gt;
  async resolve =&amp;gt; &lt;br /&gt;
        { await changeLang(config.languages[0]); &lt;br /&gt;
          resolve(); &lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hello World Vue 06d ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe: Defaultmäßig soll dem Benutzer die App in der Sprache angezeigt werden, die der Benutzer&lt;br /&gt;
im Browser voreingestellt hat. Sollte die voreingestellte Sprache nicht unterstützt werden, wird die App in der Defaultsprache angezeigt.&lt;br /&gt;
&lt;br /&gt;
Legen Sie zunächst wieder einen neuen Branch an.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v06c     # Wechsle in den Branch v06c&lt;br /&gt;
git checkout -b v06d  # Klone v06c in einen neuen Branch v06d&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Vorgehen ist im Prinzip ganz einfach.&lt;br /&gt;
&lt;br /&gt;
Mittels des NPM-Packets &amp;quot;navigator-languages&amp;quot; ermittelt man die vom Browser unterstützten&lt;br /&gt;
Sprachen.  In der Konfigurationsdatei listet man die Sprachen auf, die von der App unterstützt werden.&lt;br /&gt;
Besser wäre es natürlich, diese Liste vom Backend zu laden, damit die App mit bekommt, wenn neue&lt;br /&gt;
Sprachen zur Verfügung stehen. Am aufwändigsten ist es, diese beiden Array zu matchen.&lt;br /&gt;
&lt;br /&gt;
Mögliche Erweiterung: Auch die HTML-Pfade sollten internationalisiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm i navigator-languages // unterstützt mehrere Browservarianten&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Language Matching ===&lt;br /&gt;
&lt;br /&gt;
Das Language Matching erfolgt zweimal mit zwei verschachtelten Schleifen.&lt;br /&gt;
Zunächst wird überprüft, ob es exakte Übereinstimmungen gibt. Sollte das nicht der Fall sein, wird überprüft, ob es Matches zwischen den ersten beiden Buchstaben gibt (&#039;de&#039;, &#039;de-AT&#039;, &#039;de-DE&#039; matchen in diesem Schritt alle miteinander).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/util/chooseLanguage.js&lt;br /&gt;
&lt;br /&gt;
import navigatorLanguages from &#039;navigator-languages&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  /**&lt;br /&gt;
   * @function&lt;br /&gt;
   * @param  { Array&amp;lt;string&amp;gt; } p_langs_available &lt;br /&gt;
   *         An array of language strings containing the languages&lt;br /&gt;
   *         supported by the app &lt;br /&gt;
   * @param  { Array&amp;lt;string&amp;gt; } [p_langs_required  = navigatorLanguages()]&lt;br /&gt;
   *         An array of language strings containing the languages&lt;br /&gt;
   *         requested by the client &lt;br /&gt;
   * @return { string } &lt;br /&gt;
   *         The language to be displayed&lt;br /&gt;
   */&lt;br /&gt;
  chooseLanguage =&lt;br /&gt;
  (p_langs_available, p_langs_required = navigatorLanguages()) =&amp;gt;&lt;br /&gt;
  { let&lt;br /&gt;
      lang = p_langs_available[0],&lt;br /&gt;
      i=0, j=0, n=p_langs_required.length, m=p_langs_available.length&lt;br /&gt;
  &lt;br /&gt;
    outer:&lt;br /&gt;
    { while (i&amp;lt;n)&lt;br /&gt;
      { const c_lang_required = p_langs_required[i]&lt;br /&gt;
        while (j&amp;lt;m)&lt;br /&gt;
        { const c_lang_availabe = p_langs_available[j]&lt;br /&gt;
          if (c_lang_availabe === c_lang_required)&lt;br /&gt;
          // both languages match perfectly&lt;br /&gt;
          { lang = c_lang_availabe;&lt;br /&gt;
            break outer;&lt;br /&gt;
          }&lt;br /&gt;
          j++&lt;br /&gt;
        }&lt;br /&gt;
        i++&lt;br /&gt;
      }&lt;br /&gt;
      i=0, j=0&lt;br /&gt;
      while (i&amp;lt;n)&lt;br /&gt;
      { const c_lang_required = p_langs_required[i]&lt;br /&gt;
        while (j&amp;lt;m)&lt;br /&gt;
        { const c_lang_availabe = p_langs_available[j]&lt;br /&gt;
          if (c_lang_availabe.substring(0,2) === c_lang_required.substring(0,2))&lt;br /&gt;
          // main part of the languages match&lt;br /&gt;
          { lang = c_lang_availabe;&lt;br /&gt;
            break outer;&lt;br /&gt;
          }&lt;br /&gt;
          j++&lt;br /&gt;
        }&lt;br /&gt;
        i++&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  &lt;br /&gt;
     return lang&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
export default chooseLanguage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;src/store/StoreI18n&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Im I18N-Store wird nun beim Initialisieren nicht nicht mehr eine Defaultspache geladen, sondern &lt;br /&gt;
die Sprache, die möglichst nah an den Wünschen des im Browser des Benutzers gespeicherten Sprachwünschen liegt.&lt;br /&gt;
&lt;br /&gt;
Beachten Sie, dass Sie in die Datei &amp;lt;code&amp;gt;vite.config.common.js&amp;lt;/code&amp;gt; einen geeigneten Alias für den &lt;br /&gt;
&amp;lt;code&amp;gt;util&amp;lt;/code&amp;gt;-Pfad definieren müssen, um &amp;lt;code&amp;gt;&#039;/util/chooseLanguage&#039;&amp;lt;/code&amp;gt; an Stelle von&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;@/util/chooseLanguage&#039;&amp;lt;/code&amp;gt; schreiben zu können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
import chooseLanguage from &#039;/util/chooseLanguage&#039;&lt;br /&gt;
...&lt;br /&gt;
      () =&amp;gt;&lt;br /&gt;
      { const&lt;br /&gt;
          ...&lt;br /&gt;
          initialize = &lt;br /&gt;
             async resolve =&amp;gt; &lt;br /&gt;
                   { await changeLang(chooseLanguage(config.languages)); &lt;br /&gt;
                     resolve(); &lt;br /&gt;
                   }&lt;br /&gt;
        ...&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
== Fortsetzung des Tutoriums ==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 07|Teil 7 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
In diesem Tutorium setzen Sie Routing ein, auch wenn das für die Hello-World-Anwendung nicht sonderlich sinnvoll ist.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;!--references/--&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;1&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
	<entry>
		<id>https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_03&amp;diff=53231</id>
		<title>HTML5-Tutorium: JavaScript: Hello World Vue 03</title>
		<link rel="alternate" type="text/html" href="https://glossar.hs-augsburg.de/w/index.php?title=HTML5-Tutorium:_JavaScript:_Hello_World_Vue_03&amp;diff=53231"/>
		<updated>2025-05-12T12:57:20Z</updated>

		<summary type="html">&lt;p&gt;Kowa: /* Anpassung des Templates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}&lt;br /&gt;
&lt;br /&gt;
v03:&amp;amp;nbsp;[https://glossar.hs-augsburg.de/beispiel/tutorium/2025/wk_hello_world/wk_hello_world_vue/web_v03/index.html &amp;lt;code&amp;gt;index.html&amp;lt;/code&amp;gt;] ([https://validator.nu/?doc=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_vue%2Fweb_v03%2Findex.html HTML validate], [https://css-validator.org/validator?uri=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_vue%2Fweb_v03%2Findex.html&amp;amp;profile=css21&amp;amp;usermedium=all&amp;amp;warning=1&amp;amp;lang=de CSS validate], [https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2025%2Fwk_hello_world%2Fwk_hello_world_vue%2Fweb_v03%2Findex.html Google PageSpeed])&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
[{{Git-Server}}/kowa/wk_hello_world_vue Git-Repository], &amp;lt;code&amp;gt;git checkout v03&amp;lt;/code&amp;gt; (JavaScript)&lt;br /&gt;
&lt;br /&gt;
== Anwendungsfälle (Use Cases) ==&lt;br /&gt;
&lt;br /&gt;
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_Vue_02|ersten und zweiten Teil des Vue-Tutoriums]]&lt;br /&gt;
ändern sich die die Anwendungsfälle nicht. Die Anwendung leistet also genau dasselbe wie zuvor.&lt;br /&gt;
&lt;br /&gt;
== Aufgabe ==&lt;br /&gt;
&lt;br /&gt;
Aufgabe:  Speichere den Zustand der Anwendung (Model) in Store-Objekten,&lt;br /&gt;
um die künftige Modularisierung ([[HTML5-Tutorium: JavaScript: Hello World Vue 04|Version v04]]) zu unterstützen.&lt;br /&gt;
&lt;br /&gt;
In diesem Tutorium sollen die Daten in einem Store außerhalb der Komponenten gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
Mögliche Storekonzepte&lt;br /&gt;
* [https://vuex.vuejs.org/ vuex] (veraltet, wird nur noch gewartet, angeblich nicht mehr weiterentwickelt, aber Version 4 existiert)&lt;br /&gt;
* [https://pinia.vuejs.org/ Pinia] (der Nachfolger von vuex; einfacher zu benutzen; Vue-3- und Vue-2-Paradigmen werden unterstützt)&lt;br /&gt;
* ohne Bibliothek direkt mit &amp;lt;code&amp;gt;ref&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;reactive&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;computed&amp;lt;/code&amp;gt; aus Vue 3.2 (vgl. Vue 3 —  [https://medium.com/@mario.brendel1990/vue-3-the-new-store-a7569d4a546f The New Store])&lt;br /&gt;
&lt;br /&gt;
Wir verwenden Pinia.&lt;br /&gt;
&lt;br /&gt;
== Pinia ==&lt;br /&gt;
&lt;br /&gt;
Zitat&amp;lt;ref&amp;gt;https://vuejs.org/guide/scaling-up/state-management&amp;lt;/ref&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;While our hand-rolled state management solution will suffice in simple scenarios, there are many more things to consider in large-scale production applications:&lt;br /&gt;
&lt;br /&gt;
# Stronger conventions for team collaboration&lt;br /&gt;
# Integrating with the Vue DevTools, including timeline, in-component inspection, and time-travel debugging&lt;br /&gt;
# Hot Module Replacement&lt;br /&gt;
# Server-Side Rendering support&lt;br /&gt;
&lt;br /&gt;
Pinia is a state management library that implements all of the above. It is maintained by the Vue core team, and works with both Vue 2 and Vue 3.&lt;br /&gt;
&lt;br /&gt;
Existing users may be familiar with Vuex, the previous official state management library for Vue. With Pinia serving the same role in the ecosystem, Vuex is now in maintenance mode. It still works, but will no longer receive new features. It is recommended to use Pinia for new applications.&lt;br /&gt;
&lt;br /&gt;
Pinia started out as an exploration of what the next iteration of Vuex could look like, incorporating many ideas from core team discussions for Vuex 5. Eventually, we realized that Pinia already implements most of what we wanted in Vuex 5, and decided to make it the new recommendation instead.&lt;br /&gt;
&lt;br /&gt;
Compared to Vuex, Pinia provides a simpler API with less ceremony, offers Composition-API-style APIs, and most importantly, has solid type inference support when used with TypeScript.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Erstellen eines neuen Projektzweigs ==&lt;br /&gt;
&lt;br /&gt;
Erstellen Sie einen neuen Projektzweig (branch) innerhalb von &amp;lt;code&amp;gt;hello_world_vue&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git checkout v02     # Wechsle in den Branch v02&lt;br /&gt;
git checkout -b v03  # Klone v02 in einen neuen Branch v03&lt;br /&gt;
npm i&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Initialisierung von Pinia ==&lt;br /&gt;
&lt;br /&gt;
Pinia muss als reguläre Dependency installiert werden, da das Paket in den produktiven Code integriert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
npm i pinia  // NICHT  npm i -D pinia, da Pinia-Code an den Browser ausgeliefert werden muss&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pinia wird als Plugin in die App integriert. Dabei ist wichtig, dass das Pina-Plugin-Objekt vor dem eigentlichen App-Objekt erzeugt wird, da Komponenten der &lt;br /&gt;
App, die Pinia verwenden, darauf angewiesen sind, dass die Pinia-Erzeugung vor der Komponenten-Initialisierung erfolgt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
// src/main.js&lt;br /&gt;
&lt;br /&gt;
import { createPinia } from &#039;pinia&#039;&lt;br /&gt;
import { createApp }   from &#039;vue&#039;&lt;br /&gt;
import App             from &#039;./App.vue&#039;&lt;br /&gt;
&lt;br /&gt;
const pinia = createPinia() // before createApp(...)&lt;br /&gt;
&lt;br /&gt;
window.addEventListener&lt;br /&gt;
( &#039;load&#039;,&lt;br /&gt;
  createApp(App).use(pinia).mount(&#039;#app&#039;)&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Erstellen der Stores ==&lt;br /&gt;
&lt;br /&gt;
Fügen Sie zunächst zwei weitere Aliase in die Datei &amp;lt;code&amp;gt;vite.config.common.js&amp;lt;/code&amp;gt; ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
 &#039;/controller&#039;: fileURLToPath(new URL(&#039;./src/controller&#039;, import.meta.url)),&lt;br /&gt;
 &#039;/service&#039;:    fileURLToPath(new URL(&#039;./src/service&#039;,    import.meta.url)),&lt;br /&gt;
 &#039;/component&#039;:  fileURLToPath(new URL(&#039;./src/component&#039;,  import.meta.url)),&lt;br /&gt;
 &#039;/store&#039;:      fileURLToPath(new URL(&#039;./src/store&#039;,      import.meta.url)),&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit kann man Controller, Services. Komponenten und Stores immer mit absoluter Adressierung laden ({{zB}} in &amp;lt;code&amp;gt;App.vue&amp;lt;/code&amp;gt;), ohne &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; verwenden zu müssen.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreSession&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Im Session-Store wird die Autofocus-ID gespeichert sowie welche Section der Single-Page-Anwendung dem Benutzer aktuell präsentiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreSession.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore   } from &#039;pinia&#039;&lt;br /&gt;
import { ref, reactive } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const storeSession =&lt;br /&gt;
defineStore&lt;br /&gt;
( &#039;session&#039;, // must be unique&lt;br /&gt;
&lt;br /&gt;
  () =&amp;gt;&lt;br /&gt;
  { const&lt;br /&gt;
      section   = ref(&#039;&#039;),&lt;br /&gt;
      autofocus = reactive({ setFocus: null }),&lt;br /&gt;
&lt;br /&gt;
      init =&lt;br /&gt;
        p_section =&amp;gt; { section.value = p_section },&lt;br /&gt;
&lt;br /&gt;
      initAutofocus =&lt;br /&gt;
        (p_autofocus_id = null) =&amp;gt;&lt;br /&gt;
        { if (p_autofocus_id != null)&lt;br /&gt;
          { autofocus.setFocus = () =&amp;gt; document.getElementById(p_autofocus_id).focus(),&lt;br /&gt;
            window.addEventListener(&#039;visibilitychange&#039;, autofocus.setFocus);&lt;br /&gt;
          }&lt;br /&gt;
        },&lt;br /&gt;
&lt;br /&gt;
      setFocus =&lt;br /&gt;
        () =&amp;gt; autofocus?.setFocus()&lt;br /&gt;
&lt;br /&gt;
    return { init, initAutofocus, section, setFocus }&lt;br /&gt;
  }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
export default storeSession&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Im Greeting-Store werden die Daten zur Begrüßung des Benutzers gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
// src/store/StoreGreeting.js&lt;br /&gt;
&lt;br /&gt;
import { defineStore }               from &#039;pinia&#039;&lt;br /&gt;
import { reactive, toRef, computed } from &#039;vue&#039;&lt;br /&gt;
&lt;br /&gt;
const&lt;br /&gt;
  storeGreeting =&lt;br /&gt;
  defineStore&lt;br /&gt;
  ( &#039;greeting&#039;,&lt;br /&gt;
&lt;br /&gt;
    () =&amp;gt;&lt;br /&gt;
    { const&lt;br /&gt;
        state =&lt;br /&gt;
          reactive({ name: &#039;&#039;, stranger: &#039;Stranger&#039;, hello: &#039;Hello, $1!&#039; }),&lt;br /&gt;
&lt;br /&gt;
        init =&lt;br /&gt;
          (p_state = {}) =&amp;gt;&lt;br /&gt;
          { Object.assign(state, p_state) },&lt;br /&gt;
&lt;br /&gt;
        name = &lt;br /&gt;
          toRef(state, &#039;name&#039;),&lt;br /&gt;
&lt;br /&gt;
        reset =&lt;br /&gt;
          () =&amp;gt; { name.value = &#039;&#039; },&lt;br /&gt;
&lt;br /&gt;
        sayHello =&lt;br /&gt;
          p_name =&amp;gt; state.hello.replace(&#039;$1&#039;, p_name),&lt;br /&gt;
&lt;br /&gt;
        helloStranger =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(state.stranger)),&lt;br /&gt;
&lt;br /&gt;
        hello =&lt;br /&gt;
          computed(() =&amp;gt; sayHello(state.name))&lt;br /&gt;
&lt;br /&gt;
      return { init, name, helloStranger, hello, reset }&lt;br /&gt;
    }&lt;br /&gt;
  )&lt;br /&gt;
&lt;br /&gt;
export default storeGreeting&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Einbinden der Stores in eine Komponente==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script setup&amp;gt;&lt;br /&gt;
  import { useId, onMounted, onUnmounted } from &#039;vue&#039;&lt;br /&gt;
  import storeSession   from &#039;/store/StoreSession&#039;&lt;br /&gt;
  import storeGreeting  from &#039;/store/StoreGreeting&#039;&lt;br /&gt;
  import ControllerKeys from &#039;/controller/ControllerKeys&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    props           = defineProps ({ autofocus: { type: Boolean } }),&lt;br /&gt;
    inputId         = useId(),&lt;br /&gt;
&lt;br /&gt;
    session         = storeSession(),&lt;br /&gt;
    greeting        = storeGreeting(),&lt;br /&gt;
&lt;br /&gt;
    reset           = () =&amp;gt; { greeting.reset(); session.setFocus() },&lt;br /&gt;
    sayHello        = () =&amp;gt; session.section = &#039;hello&#039;,&lt;br /&gt;
    sayHelloOnEnter = new ControllerKeys(&#039;Enter&#039;, inputId, sayHello)&lt;br /&gt;
&lt;br /&gt;
  session.init(&#039;form&#039;, props.autofocus ? inputId : null)&lt;br /&gt;
  //greeting.init({ hello: &#039;Hi, $1!&#039;, stranger: &#039;Nobody&#039;})&lt;br /&gt;
&lt;br /&gt;
  onMounted   ( () =&amp;gt; sayHelloOnEnter.add()    )&lt;br /&gt;
  onUnmounted ( () =&amp;gt; sayHelloOnEnter.remove() )&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Vergleich noch einmal das Skript ohne Store:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/component/HelloWorld.vue without store --&amp;gt;&lt;br /&gt;
&amp;lt;script setup &amp;gt;&lt;br /&gt;
  import { useId, ref, computed, onMounted, onUnmounted } from &#039;vue&#039;&lt;br /&gt;
  import ControllerKeys from &#039;@/controller/ControllerKeys&#039;&lt;br /&gt;
&lt;br /&gt;
  const&lt;br /&gt;
    props         = defineProps ({ autofocus: { type: Boolean } }),&lt;br /&gt;
    inputId       = useId(),&lt;br /&gt;
    section       = ref(&#039;form&#039;),&lt;br /&gt;
    helloStranger = ref(&#039;Hello, Stranger&#039;),&lt;br /&gt;
    name          = ref(&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
    hello     = computed(() =&amp;gt; name.value === &#039;&#039; ? helloStranger.value : `Hello, ${name.value}!`),&lt;br /&gt;
    sayHello  = () =&amp;gt; section.value = &#039;hello&#039;,&lt;br /&gt;
    autoFocus = () =&amp;gt; { if (props.autofocus)&lt;br /&gt;
                          document.getElementById(inputId).focus()&lt;br /&gt;
                      },&lt;br /&gt;
    reset     = () =&amp;gt; { document.getElementById(inputId).value = &#039;&#039;;&lt;br /&gt;
                        autoFocus();&lt;br /&gt;
                      },&lt;br /&gt;
    sayHelloOnEnter = new ControllerKeys(&#039;Enter&#039;, inputId, sayHello)&lt;br /&gt;
&lt;br /&gt;
  onMounted  (...)&lt;br /&gt;
  onUnmounted(...)&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anpassung des Templates ==&lt;br /&gt;
&lt;br /&gt;
Das Template muss angepasst werden. Man muss nun auf die Store-Objekte zugreifen.&lt;br /&gt;
&lt;br /&gt;
Achtung, die Funktion &amp;lt;code&amp;gt;reset&amp;lt;/code&amp;gt; aktivier nicht nur das Autofokus-Element, &lt;br /&gt;
sondern löscht auch das NAmensfeld im &amp;lt;code&amp;gt;StoreGreeting&amp;lt;/code&amp;gt;. Würde man in der &lt;br /&gt;
Funktion &amp;lt;code&amp;gt;reset&amp;lt;/code&amp;gt; auf den Aufruf von &amp;lt;code&amp;gt;storeGreeting.reset()&amp;lt;/code&amp;gt; verzichten,&lt;br /&gt;
würde der Input-Type &amp;lt;code&amp;gt;reset&amp;lt;/code&amp;gt; im unten angegebenen Input-Buttons zwar &lt;br /&gt;
den Inhalt Textfelds im Formular löschen, der zuvor eingegebenen Name stünde aber weiterhin im Greeting-Store. Die bereitet spätestens dann Probleme, wenn man den im Greeting-Store gespeicherten Namen auch a anderer Stelle verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ecmascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!-- src/components/HelloWorld.vue --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;template&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;form&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.helloStranger}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;label :for=&amp;quot;inputId&amp;quot;&amp;gt;What´s your name?&amp;lt;/label&amp;gt;&lt;br /&gt;
        &amp;lt;input :id=&amp;quot;inputId&amp;quot; v-model=&amp;quot;greeting.name&amp;quot; :autofocus&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
      &amp;lt;div&amp;gt;&lt;br /&gt;
        &amp;lt;input type=&amp;quot;reset&amp;quot;  value=&amp;quot;Reset&amp;quot;     @click=&amp;quot;reset&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;input type=&amp;quot;button&amp;quot; value=&amp;quot;Say hello&amp;quot; @click=&amp;quot;sayHello&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
  &amp;lt;section v-if=&amp;quot;session.section===&#039;hello&#039;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h1&amp;gt;{{greeting.hello}}&amp;lt;/h1&amp;gt;&lt;br /&gt;
    &amp;lt;p&amp;gt;Welcome to Full-Stack Web Development!&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;/template&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Fortsetzung des Tutoriums==&lt;br /&gt;
&lt;br /&gt;
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World Vue 04|Teil 4 des Vue-Tutoriums]] bearbeiten.&lt;br /&gt;
Nun wird die Anwendung modularisiert.&lt;br /&gt;
&lt;br /&gt;
==Quellen==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li value=&amp;quot;2&amp;quot;&amp;gt; {{Quelle|Kowarschick, W.: Web-Programmierung}}&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
→ &amp;lt;code&amp;gt;&amp;lt;/code&amp;gt; „&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;“&lt;br /&gt;
--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kowa</name></author>
	</entry>
</feed>