diff --git a/api/collections/content.yml b/api/collections/content.yml index 93a9aa7..3c04ea8 100644 --- a/api/collections/content.yml +++ b/api/collections/content.yml @@ -24,14 +24,18 @@ meta: mediaQuery: "(max-width:599px)" primaryText: path columns: - - name - - path + - source: path + twig: "{{locale}}{{path}}" + - source: locale + type: flag # Desktop - type: table mediaQuery: "(min-width:600px)" columns: - - name - - path + - source: path + twig: "{{locale}}{{path}}" + - source: locale + type: flag imageFilter: xs: @@ -102,6 +106,7 @@ hooks: # Feldliste der Kollektion fields: + - !include fields/_locale.yml - name: name type: string meta: diff --git a/api/collections/general.yml b/api/collections/general.yml index 47c8c9f..8373452 100644 --- a/api/collections/general.yml +++ b/api/collections/general.yml @@ -34,222 +34,225 @@ meta: - lastname - companyName - entryViewFields: - tabsSection: - meta: - expand: meta - tabs: - - name: general - meta: - label: { de: "Allgemein", en: "General" } - fields: - - name: public - type: boolean - meta: - defaultValue: true - label: - de: Veröffentlicht - en: Public - helperText: - de: "Alle allgemeinen Informationen werden auf der Seite angezeigt." - en: "All general information are displayed on the page." - - name: meta - meta: - label: { de: "Meta", en: "Meta" } - fields: - - name: meta - type: object - subFields: - - name: metaTitle - type: string - meta: - label: { de: "Titel der Webseite", en: "Page Title" } - - name: metaDescription - type: string - meta: - label: { de: "Beschreibung der Webseite", en: "Page Description" } - - name: metaTagRobots - type: string[] - meta: - widget: chipArray - label: - de: Robots - en: Robots - defaultValue: [] - autocomplete: true - choices: - - { id: "noindex", name: "noindex" } - - { id: "index", name: "index" } - - { id: "follow", name: "follow" } - - { id: "nofollow", name: "nofollow" } - - { id: "noimageindex", name: "noimageindex" } - - { id: "none", name: "none" } - - { id: "noarchive", name: "noarchive" } - - { id: "nocache", name: "nocache" } - - { id: "nosnippet", name: "nosnippet" } - - { id: "nnavailable_after", name: "nnavailable_after" } - helperText: - de: "Noindex: Weist eine Suchmaschine an, eine Seite nicht zu indizieren.
index: Weist eine Suchmaschine an, eine Seite zu indizieren. Beachten Sie, dass Sie dieses Meta-Tag nicht hinzufügen müssen; es ist die Voreinstellung.
follow: Auch wenn die Seite nicht indexiert ist, sollte der Crawler allen Links auf einer Seite folgen und Eigenkapital an die verlinkten Seiten weitergeben.
nofollow: Weist einen Crawler an, keinen Links auf einer Seite zu folgen oder Link-Equity weiterzugeben.
noimageindex: Weist einen Crawler an, keine Bilder auf einer Seite zu indizieren.
none: Entspricht der gleichzeitigen Verwendung der noindex- und nofollow-Tags.
noarchive: Suchmaschinen sollten keinen zwischengespeicherten Link zu dieser Seite auf einem SERP anzeigen.
nocache: Wie noarchive, aber nur von Internet Explorer und Firefox verwendet.
nosnippet: Weist eine Suchmaschine an, kein Snippet dieser Seite (d. h. Meta-Beschreibung) dieser Seite auf einem SERP anzuzeigen.
nnavailable_after: Suchmaschinen sollen diese Seite nach einem bestimmten Datum nicht mehr indexieren.
" - en: "Noindex: Tells a search engine not to index a page.
index: Tells a search engine to index a page. Note that you don’t need to add this meta tag; it’s the default.
follow: Even if the page isn’t indexed, the crawler should follow all the links on a page and pass equity to the linked pages.
nofollow: Tells a crawler not to follow any links on a page or pass along any link equity.
noimageindex: Tells a crawler not to index any images on a page.
none: Equivalent to using both the noindex and nofollow tags simultaneously.
noarchive: Search engines should not show a cached link to this page on a SERP.
nocache: Same as noarchive, but only used by Internet Explorer and Firefox.
nosnippet: Tells a search engine not to show a snippet of this page (i.e. meta description) of this page on a SERP.
nnavailable_after: Search engines should no longer index this page after a particular date.
" - - name: metaKeywords - type: string - meta: - label: { de: "SEO / Schlüsselwörter", en: "SEO / Keywords" } - helperText: - de: "Beispiel: Stichwort1, Stichwort2, Stichwort3" - en: "Example: keyword1, keyword2, keyword3" - - name: person - meta: - label: { de: "Person", en: "Person" } - fields: - - name: person - type: object - subFields: - - name: salutation - type: string - meta: - label: { de: "Anrede", en: "Salutation" } - - name: firstname - type: string - meta: - label: { de: "Vorname", en: "Firstname" } - - name: lastname - type: string - meta: - label: { de: "Nachname", en: "Lastname" } - - name: additional - type: string - meta: - label: { de: "Zusatz", en: "Additional" } - - name: street - type: string - meta: - label: { de: "Straße", en: "Street" } - - name: postcode - type: string - meta: - label: { de: "Postleitzahl", en: "Postcode" } - - name: city - type: string - meta: - label: { de: "Ort", en: "City" } - - name: tel - type: string - meta: - label: { de: "Telefonnummer", en: "Phone number" } - - name: fax - type: string - meta: - label: { de: "Faxnummer", en: "Fax number" } - - name: mobile - type: string - meta: - label: { de: "Handynummer", en: "Mobile number" } - - name: email - type: string - meta: - label: { de: "E-Mail", en: "E-Mail" } - - name: company - meta: - label: { de: "Unternehmen", en: "Company" } - fields: - - name: person - type: object - subFields: - - name: companyName - type: string - meta: - label: { de: "Name des Unternehmens", en: "Company Name" } - - name: companyWebUrl - type: string - meta: - label: { de: "URL zur Webseite", en: "Website URL" } - - name: companyAddresses - type: object[] - meta: - label: - de: Adresse - en: Adresse - css: - subFields: - - name: street - type: string - meta: - label: { de: "Straße", en: "Street" } - - name: houseNr - type: string - meta: - label: { de: "Hausnummer", en: "House number" } - - name: postcode - type: string - meta: - label: { de: "PLZ", en: "ZIP" } - - name: city - type: string - meta: - label: { de: "Ort", en: "City" } - - name: tel - type: string - meta: - label: { de: "Telefon", en: "Phone number" } - - name: fax - type: string - meta: - label: { de: "Fax", en: "Fax" } - - name: email - type: string - meta: - label: { de: "E-Mail", en: "E-Mail" } - - name: media - meta: - label: { de: "Medien", en: "Media" } - fields: - - name: media - type: object - subFields: - - name: favicon - type: file - meta: - label: { de: "Favicon", en: "Favicon" } - helperText: - de: "Ein Favicon ist ein kleines Icon, Symbol oder Logo, das von Webbrowsern verwendet wird, um eine Website auf wiedererkennbare Weise zu kennzeichnen." - en: "A favicon is a small icon, symbol, or logo used by web browsers to identify a website in a recognizable way." - - name: brand - type: file - meta: - label: { de: "Logo / Brand", en: "Logo / Brand" } - helperText: - de: "Logo der Seite" - en: "Page Logo" - - name: mediaFiles - type: object[] - meta: - label: { de: "Dateien", en: "Files" } - subFields: - - name: title - type: string - meta: - label: { de: "Datei-Titel", en: "File Title" } - - name: alternateText - type: string - meta: - label: { de: "Alternativer Text", en: "Alternate Text" } - - name: id - type: string - meta: - label: { de: "Technischer Name / ID", en: "Technical name / ID" } - - name: file - type: file - meta: - label: { de: "", en: "" } - - name: copyright - meta: - label: { de: "Copyright", en: "Copyright" } - fields: - - name: copyrightText - type: string - meta: - label: { de: "Copyright Text", en: "Copyright Text" } + tabsSection: + meta: + expand: media + tabs: + - name: general + meta: + label: { de: "Allgemein", en: "General" } + fields: + - name: public + type: boolean + meta: + defaultValue: true + label: + de: Veröffentlicht + en: Public + helperText: + de: "Alle allgemeinen Informationen werden auf der Seite angezeigt." + en: "All general information are displayed on the page." + - name: meta + meta: + label: { de: "Meta", en: "Meta" } + fields: + - name: meta + type: object + subFields: + - name: metaTitle + type: string + meta: + label: { de: "Titel der Webseite", en: "Page Title" } + - name: metaDescription + type: string + meta: + label: { de: "Beschreibung der Webseite", en: "Page Description" } + - name: metaTagRobots + type: string[] + meta: + widget: chipArray + label: + de: Robots + en: Robots + defaultValue: [] + autocomplete: true + choices: + - { id: "noindex", name: "noindex" } + - { id: "index", name: "index" } + - { id: "follow", name: "follow" } + - { id: "nofollow", name: "nofollow" } + - { id: "noimageindex", name: "noimageindex" } + - { id: "none", name: "none" } + - { id: "noarchive", name: "noarchive" } + - { id: "nocache", name: "nocache" } + - { id: "nosnippet", name: "nosnippet" } + - { id: "nnavailable_after", name: "nnavailable_after" } + helperText: + de: "Noindex: Weist eine Suchmaschine an, eine Seite nicht zu indizieren.
index: Weist eine Suchmaschine an, eine Seite zu indizieren. Beachten Sie, dass Sie dieses Meta-Tag nicht hinzufügen müssen; es ist die Voreinstellung.
follow: Auch wenn die Seite nicht indexiert ist, sollte der Crawler allen Links auf einer Seite folgen und Eigenkapital an die verlinkten Seiten weitergeben.
nofollow: Weist einen Crawler an, keinen Links auf einer Seite zu folgen oder Link-Equity weiterzugeben.
noimageindex: Weist einen Crawler an, keine Bilder auf einer Seite zu indizieren.
none: Entspricht der gleichzeitigen Verwendung der noindex- und nofollow-Tags.
noarchive: Suchmaschinen sollten keinen zwischengespeicherten Link zu dieser Seite auf einem SERP anzeigen.
nocache: Wie noarchive, aber nur von Internet Explorer und Firefox verwendet.
nosnippet: Weist eine Suchmaschine an, kein Snippet dieser Seite (d. h. Meta-Beschreibung) dieser Seite auf einem SERP anzuzeigen.
nnavailable_after: Suchmaschinen sollen diese Seite nach einem bestimmten Datum nicht mehr indexieren.
" + en: "Noindex: Tells a search engine not to index a page.
index: Tells a search engine to index a page. Note that you don’t need to add this meta tag; it’s the default.
follow: Even if the page isn’t indexed, the crawler should follow all the links on a page and pass equity to the linked pages.
nofollow: Tells a crawler not to follow any links on a page or pass along any link equity.
noimageindex: Tells a crawler not to index any images on a page.
none: Equivalent to using both the noindex and nofollow tags simultaneously.
noarchive: Search engines should not show a cached link to this page on a SERP.
nocache: Same as noarchive, but only used by Internet Explorer and Firefox.
nosnippet: Tells a search engine not to show a snippet of this page (i.e. meta description) of this page on a SERP.
nnavailable_after: Search engines should no longer index this page after a particular date.
" + - name: metaKeywords + type: string + meta: + label: { de: "SEO / Schlüsselwörter", en: "SEO / Keywords" } + helperText: + de: "Beispiel: Stichwort1, Stichwort2, Stichwort3" + en: "Example: keyword1, keyword2, keyword3" + - name: person + meta: + label: { de: "Person", en: "Person" } + fields: + - name: person + type: object + subFields: + - name: salutation + type: string + meta: + label: { de: "Anrede", en: "Salutation" } + - name: firstname + type: string + meta: + label: { de: "Vorname", en: "Firstname" } + - name: lastname + type: string + meta: + label: { de: "Nachname", en: "Lastname" } + - name: additional + type: string + meta: + label: { de: "Zusatz", en: "Additional" } + - name: street + type: string + meta: + label: { de: "Straße", en: "Street" } + - name: postcode + type: string + meta: + label: { de: "Postleitzahl", en: "Postcode" } + - name: city + type: string + meta: + label: { de: "Ort", en: "City" } + - name: tel + type: string + meta: + label: { de: "Telefonnummer", en: "Phone number" } + - name: fax + type: string + meta: + label: { de: "Faxnummer", en: "Fax number" } + - name: mobile + type: string + meta: + label: { de: "Handynummer", en: "Mobile number" } + - name: email + type: string + meta: + label: { de: "E-Mail", en: "E-Mail" } + - name: company + meta: + label: { de: "Unternehmen", en: "Company" } + fields: + - name: person + type: object + subFields: + - name: companyName + type: string + meta: + label: { de: "Name des Unternehmens", en: "Company Name" } + - name: companyWebUrl + type: string + meta: + label: { de: "URL zur Webseite", en: "Website URL" } + - name: companyAddresses + type: object[] + meta: + label: + de: Adresse + en: Adresse + css: + subFields: + - name: street + type: string + meta: + label: { de: "Straße", en: "Street" } + - name: houseNr + type: string + meta: + label: { de: "Hausnummer", en: "House number" } + - name: postcode + type: string + meta: + label: { de: "PLZ", en: "ZIP" } + - name: city + type: string + meta: + label: { de: "Ort", en: "City" } + - name: tel + type: string + meta: + label: { de: "Telefon", en: "Phone number" } + - name: fax + type: string + meta: + label: { de: "Fax", en: "Fax" } + - name: email + type: string + meta: + label: { de: "E-Mail", en: "E-Mail" } + - name: media + meta: + label: { de: "Medien", en: "Media" } + fields: + - name: media + type: object + subFields: + - name: favicon + type: file + meta: + label: { de: "Favicon", en: "Favicon" } + helperText: + de: "Ein Favicon ist ein kleines Icon, Symbol oder Logo, das von Webbrowsern verwendet wird, um eine Website auf wiedererkennbare Weise zu kennzeichnen." + en: "A favicon is a small icon, symbol, or logo used by web browsers to identify a website in a recognizable way." + - name: brand + type: file + meta: + label: { de: "Logo / Brand", en: "Logo / Brand" } + helperText: + de: "Logo der Seite" + en: "Page Logo" + - name: mediaFiles + type: object[] + meta: + label: { de: "Dateien", en: "Files" } + subFields: + - name: title + type: string + meta: + label: { de: "Datei-Titel", en: "File Title" } + - name: alternateText + type: string + meta: + label: { de: "Alternativer Text", en: "Alternate Text" } + - name: id + type: string + meta: + editableWhileCreating: true + label: { de: "Technischer Name / ID", en: "Technical name / ID" } + helperText: + de: "Achtung: Eine technische ID ist meist fest im Code der Seite verknüpft und darf, wenn sie einmal gesetzt wurde, nicht verändert werden!" + en: "Attention: A technical ID is usually permanently linked in the code of the page and, once it has been set, must not be changed!" + - name: file + type: file + meta: + label: { de: "Datei", en: "File" } + - name: copyright + meta: + label: { de: "Copyright", en: "Copyright" } + fields: + - name: copyrightText + type: string + meta: + label: { de: "Copyright Text", en: "Copyright Text" } imageFilter: xs: diff --git a/api/collections/navigation.yml b/api/collections/navigation.yml index bc69477..5b71f5b 100644 --- a/api/collections/navigation.yml +++ b/api/collections/navigation.yml @@ -204,3 +204,4 @@ indexes: key: - ident - locale + - path diff --git a/src/api.ts b/src/api.ts index 977ef7f..be4263a 100644 --- a/src/api.ts +++ b/src/api.ts @@ -186,8 +186,11 @@ export const sendEmail = async (type: string = "contactForm", data: any, noToken }) } -export const getContent = async (path: string): Promise => { - const c = await api("content", { limit: 1, filter: { path } }) +export const getContent = async (path: string, lang: string): Promise => { + const c = await api("content", { + limit: 1, + filter: { path: path.replace("/" + lang, ""), locale: lang }, + }) if (c?.data?.length) { return c.data[0] } diff --git a/src/components/App.svelte b/src/components/App.svelte index e21e0fb..aa22c84 100644 --- a/src/components/App.svelte +++ b/src/components/App.svelte @@ -1,7 +1,7 @@ + + {#if $generalInfo?.meta?.metaTitle} + {$generalInfo?.meta?.metaTitle} + {/if} + {#if $generalInfo?.meta?.metaDescription} + + {/if} + {#if $generalInfo?.meta?.metaKeywords} + + {/if} + {#if $generalInfo?.person?.firstname || $generalInfo?.person?.lastname} + + {/if} + {#if $generalInfo?.meta?.metaTagRobots} + + {/if} + {#if $generalInfo?.media?.favicon} + + {/if} + +
diff --git a/src/components/routes/Content.svelte b/src/components/routes/Content.svelte index e0645d9..62e2b0d 100644 --- a/src/components/routes/Content.svelte +++ b/src/components/routes/Content.svelte @@ -1,7 +1,6 @@ - - {$generalInfo?.meta?.metaTitle} - - - - - +
+
+
+ +
+
diff --git a/src/components/widgets/GeneralMediaImage.svelte b/src/components/widgets/GeneralMediaImage.svelte new file mode 100644 index 0000000..1516caa --- /dev/null +++ b/src/components/widgets/GeneralMediaImage.svelte @@ -0,0 +1,20 @@ + + +{#if id} + {#each $generalInfo?.media?.mediaFiles || [] as mediaFile} + {#if mediaFile.id === id && mediaFile.file} + {#if mediaFile.file.src.includes(";base64,")} + {mediaFile.alternateText ? mediaFile.alternateText + ' - ' : ''} + {/if} + {/if} + {/each} +{/if} diff --git a/src/components/widgets/LanguageChooser.svelte b/src/components/widgets/LanguageChooser.svelte new file mode 100644 index 0000000..33abfc4 --- /dev/null +++ b/src/components/widgets/LanguageChooser.svelte @@ -0,0 +1,30 @@ + + +
+ {#each languages as language} +
+ {language} +
+ {/each} +
diff --git a/src/components/widgets/Navigation.svelte b/src/components/widgets/Navigation.svelte index 12d9366..dcc7e29 100644 --- a/src/components/widgets/Navigation.svelte +++ b/src/components/widgets/Navigation.svelte @@ -2,11 +2,12 @@ import * as animateScroll from "svelte-scrollto" import Icon from "mdi-svelte" import { mdiMenu } from "@mdi/js" - // import { generalInfo } from "../../store" import { links } from "svelte-routing" import { navigations } from "../../store" + import LanguageChooser from "./LanguageChooser.svelte" + export let ident = "main" let navigation: Navigation @@ -21,6 +22,8 @@ } + + {#if navigation}
{:else} diff --git a/src/components/widgets/ScrollTo.svelte b/src/components/widgets/ScrollTo.svelte index a73fd01..68b3068 100644 --- a/src/components/widgets/ScrollTo.svelte +++ b/src/components/widgets/ScrollTo.svelte @@ -1,6 +1,8 @@ @@ -14,7 +16,7 @@ }) }}" > - +
@@ -26,12 +28,12 @@ }) }}" > - +
diff --git a/src/css/theme-2022/components/general.less b/src/css/theme-2022/components/general.less index c336783..0dd46c3 100755 --- a/src/css/theme-2022/components/general.less +++ b/src/css/theme-2022/components/general.less @@ -19,39 +19,6 @@ body { } } -// Body Images - -.body-image-left { - position: absolute; - left: 0; - top: 80rem; - - @media (max-width: 900px) { - top: 90rem; - } -} - -// Lists - -ul { - list-style-type: none; - padding-left: 15px; - - li { - line-height: 30px; - padding-left: 0; - font-weight: 700; - - &:before { - content: "\203A"; - font-weight: 700; - position: absolute; - top: -1px; - left: -12px; - } - } -} - // Scroll To Top .scroll-to-top { @@ -96,13 +63,3 @@ ul { right: -53px; } } - -// Promotion - -.promotion-image-wave-top { - height: 70px; -} - -.promotion-image-wave-bottom { - height: 137px; -} diff --git a/src/css/theme-2022/components/language-chooser.less b/src/css/theme-2022/components/language-chooser.less new file mode 100644 index 0000000..cfe3887 --- /dev/null +++ b/src/css/theme-2022/components/language-chooser.less @@ -0,0 +1,22 @@ +.language-chooser { + display: flex; + justify-content: flex-end; + align-items: center; + border: 1px solid #ccc; + + .lang { + border: 1px solid #ccc; + cursor: pointer; + + &:hover { + background: #ccc; + color: black; + } + + &.active { + background: #ccc; + color: black; + font-weight: 700; + } + } +} diff --git a/src/css/theme-2022/main.less b/src/css/theme-2022/main.less index e16e29b..8a809ca 100644 --- a/src/css/theme-2022/main.less +++ b/src/css/theme-2022/main.less @@ -53,5 +53,6 @@ @import "components/history"; @import "components/top-section"; @import "components/forms"; +@import "components/language-chooser"; @import "components/cc-bar"; diff --git a/src/css/theme-2022/reset.less b/src/css/theme-2022/reset.less index 23a118c..04458d0 100644 --- a/src/css/theme-2022/reset.less +++ b/src/css/theme-2022/reset.less @@ -49,6 +49,7 @@ a:not([class]) { /* Make images easier to work with */ img, picture { + width: 100%; max-width: 100%; display: block; } diff --git a/src/store.ts b/src/store.ts index 19880f5..75e3148 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,20 +1,25 @@ import { writable, get } from "svelte/store" import { getGeneralInformation, getArticles, getNavigations } from "./api" -const initLoc = { +// Localtion + +const initialLocation = { path: (typeof window !== "undefined" && window.location?.pathname) || "/", search: (typeof window !== "undefined" && window.location?.search) || "", push: false, pop: false, } +export const location = writable(initialLocation) -export const location = writable(initLoc) +// Current Language -const locale = { - key: "de", - title: "Deutsch", +function langFromUrl(): string { + if (new RegExp(`\/[a-zA-Z]*[-[a-zA-Z]*]{0,1}\/[a-zA-Z0-9-]*$`).test(get(location).path)) { + return get(location)?.path.split("/")[1] + } + return "de" } -export const currentLocale = writable(locale) +export const currentLang = writable(langFromUrl()) // General Information @@ -43,7 +48,7 @@ const getAllNavigations = async (locale: Locale) => { const list = await getNavigations(locale) navigations.set(list) } -getAllNavigations(locale) +getAllNavigations({ key: get(currentLang) }) // Cookies - Webmakers Cookie Bar diff --git a/types/global.d.ts b/types/global.d.ts index e65939e..727d208 100644 --- a/types/global.d.ts +++ b/types/global.d.ts @@ -117,7 +117,7 @@ interface NavigationItem { interface Locale { key: string - title: string + title?: string } interface GeneralImage {