Updated language selector

This commit is contained in:
becarta
2025-02-18 01:53:19 +01:00
parent 87d61929dc
commit b9c54ae77d
12 changed files with 796 additions and 195 deletions

View File

@@ -6,6 +6,7 @@ import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
import tailwind from '@astrojs/tailwind';
import mdx from '@astrojs/mdx';
import react from '@astrojs/react';
import partytown from '@astrojs/partytown';
import icon from 'astro-icon';
import compress from 'astro-compress';
@@ -29,6 +30,7 @@ export default defineConfig({
},
integrations: [
react(),
tailwind({
applyBaseStyles: false,
}),
@@ -48,6 +50,7 @@ export default defineConfig({
'business-contact',
'database',
],
'circle-flags': ['*'],
},
}),

522
package-lock.json generated
View File

@@ -9,16 +9,21 @@
"version": "1.0.0-beta.50",
"dependencies": {
"@astrojs/netlify": "^6.1.0",
"@astrojs/react": "^4.2.0",
"@astrojs/rss": "^4.0.11",
"@astrojs/sitemap": "^3.2.1",
"@astrolib/analytics": "^0.6.1",
"@astrolib/seo": "^1.0.0-beta.8",
"@fontsource-variable/inter": "^5.1.1",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"astro": "^5.2.3",
"astro-embed": "^0.9.0",
"astro-icon": "^1.1.5",
"limax": "4.1.0",
"lodash.merge": "^4.6.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"unpic": "^3.22.0"
},
"devDependencies": {
@@ -27,6 +32,7 @@
"@astrojs/partytown": "^2.1.3",
"@astrojs/tailwind": "^6.0.0",
"@eslint/js": "^9.18.0",
"@iconify-json/circle-flags": "^1.2.6",
"@iconify-json/flat-color-icons": "^1.2.1",
"@iconify-json/tabler": "^1.2.14",
"@tailwindcss/typography": "^0.5.16",
@@ -76,6 +82,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@ampproject/remapping": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
"license": "Apache-2.0",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.24"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@antfu/install-pkg": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-0.4.0.tgz",
@@ -397,6 +416,26 @@
"node": "^18.17.1 || ^20.3.0 || >=22.0.0"
}
},
"node_modules/@astrojs/react": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@astrojs/react/-/react-4.2.0.tgz",
"integrity": "sha512-2OccnYFK+mLuy9GpJqPM3BQGvvemnXNeww+nBVYFuiH04L7YIdfg4Gq0LT7v/BraiuADV5uTl9VhTDL/ZQPAhw==",
"license": "MIT",
"dependencies": {
"@vitejs/plugin-react": "^4.3.4",
"ultrahtml": "^1.5.3",
"vite": "^6.0.9"
},
"engines": {
"node": "^18.17.1 || ^20.3.0 || >=22.0.0"
},
"peerDependencies": {
"@types/react": "^17.0.50 || ^18.0.21 || ^19.0.0",
"@types/react-dom": "^17.0.17 || ^18.0.6 || ^19.0.0",
"react": "^17.0.2 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/@astrojs/rss": {
"version": "4.0.11",
"resolved": "https://registry.npmjs.org/@astrojs/rss/-/rss-4.0.11.tgz",
@@ -539,28 +578,210 @@
"zod": "^3.23.8"
}
},
"node_modules/@babel/code-frame": {
"version": "7.26.2",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.25.9",
"js-tokens": "^4.0.0",
"picocolors": "^1.0.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/compat-data": {
"version": "7.26.8",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz",
"integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
"version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz",
"integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==",
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.26.2",
"@babel/generator": "^7.26.9",
"@babel/helper-compilation-targets": "^7.26.5",
"@babel/helper-module-transforms": "^7.26.0",
"@babel/helpers": "^7.26.9",
"@babel/parser": "^7.26.9",
"@babel/template": "^7.26.9",
"@babel/traverse": "^7.26.9",
"@babel/types": "^7.26.9",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
"json5": "^2.2.3",
"semver": "^6.3.1"
},
"engines": {
"node": ">=6.9.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/babel"
}
},
"node_modules/@babel/core/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/@babel/generator": {
"version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz",
"integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.26.9",
"@babel/types": "^7.26.9",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
"jsesc": "^3.0.2"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-compilation-targets": {
"version": "7.26.5",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz",
"integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==",
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.26.5",
"@babel/helper-validator-option": "^7.25.9",
"browserslist": "^4.24.0",
"lru-cache": "^5.1.1",
"semver": "^6.3.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"license": "ISC",
"dependencies": {
"yallist": "^3.0.2"
}
},
"node_modules/@babel/helper-compilation-targets/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/@babel/helper-compilation-targets/node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"license": "ISC"
},
"node_modules/@babel/helper-module-imports": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
"integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
"license": "MIT",
"dependencies": {
"@babel/traverse": "^7.25.9",
"@babel/types": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
"version": "7.26.0",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz",
"integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==",
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9",
"@babel/traverse": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
}
},
"node_modules/@babel/helper-plugin-utils": {
"version": "7.26.5",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz",
"integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.25.7",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz",
"integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==",
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
"integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.25.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz",
"integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==",
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
"integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-option": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz",
"integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helpers": {
"version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz",
"integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==",
"license": "MIT",
"dependencies": {
"@babel/template": "^7.26.9",
"@babel/types": "^7.26.9"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.25.4",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz",
"integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==",
"version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz",
"integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==",
"license": "MIT",
"dependencies": {
"@babel/types": "^7.25.4"
"@babel/types": "^7.26.9"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -569,14 +790,85 @@
"node": ">=6.0.0"
}
},
"node_modules/@babel/types": {
"version": "7.25.7",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz",
"integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==",
"node_modules/@babel/plugin-transform-react-jsx-self": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz",
"integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.7",
"@babel/helper-validator-identifier": "^7.25.7",
"to-fast-properties": "^2.0.0"
"@babel/helper-plugin-utils": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/plugin-transform-react-jsx-source": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz",
"integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==",
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/template": {
"version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz",
"integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.26.2",
"@babel/parser": "^7.26.9",
"@babel/types": "^7.26.9"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
"version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz",
"integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.26.2",
"@babel/generator": "^7.26.9",
"@babel/parser": "^7.26.9",
"@babel/template": "^7.26.9",
"@babel/types": "^7.26.9",
"debug": "^4.3.1",
"globals": "^11.1.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse/node_modules/globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/types": {
"version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz",
"integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
@@ -1259,6 +1551,16 @@
"url": "https://github.com/sponsors/nzakas"
}
},
"node_modules/@iconify-json/circle-flags": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@iconify-json/circle-flags/-/circle-flags-1.2.6.tgz",
"integrity": "sha512-UR7hL1nxOFWDVlSTuAmssyAzm3U1PuE5CMVLlahuVsnm1l7Q2b29+jUaSLTDCFqrpvdeWifCmj4C6Ojkj+4uww==",
"dev": true,
"license": "MIT",
"dependencies": {
"@iconify/types": "*"
}
},
"node_modules/@iconify-json/flat-color-icons": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@iconify-json/flat-color-icons/-/flat-color-icons-1.2.1.tgz",
@@ -1727,7 +2029,6 @@
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.5",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@jridgewell/set-array": "^1.2.1",
@@ -1740,7 +2041,6 @@
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -1748,7 +2048,6 @@
},
"node_modules/@jridgewell/set-array": {
"version": "1.2.1",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
@@ -1770,7 +2069,6 @@
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
@@ -2387,6 +2685,47 @@
"@types/estree": "*"
}
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
"integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.20.7",
"@babel/types": "^7.20.7",
"@types/babel__generator": "*",
"@types/babel__template": "*",
"@types/babel__traverse": "*"
}
},
"node_modules/@types/babel__generator": {
"version": "7.6.8",
"resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz",
"integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==",
"license": "MIT",
"dependencies": {
"@babel/types": "^7.0.0"
}
},
"node_modules/@types/babel__template": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
"integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.1.0",
"@babel/types": "^7.0.0"
}
},
"node_modules/@types/babel__traverse": {
"version": "7.20.6",
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz",
"integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==",
"license": "MIT",
"dependencies": {
"@babel/types": "^7.20.7"
}
},
"node_modules/@types/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
@@ -2520,6 +2859,24 @@
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
},
"node_modules/@types/react": {
"version": "19.0.10",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz",
"integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==",
"license": "MIT",
"dependencies": {
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-dom": {
"version": "19.0.4",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz",
"integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==",
"license": "MIT",
"peerDependencies": {
"@types/react": "^19.0.0"
}
},
"node_modules/@types/sax": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz",
@@ -2834,6 +3191,25 @@
"node": ">=8"
}
},
"node_modules/@vitejs/plugin-react": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz",
"integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==",
"license": "MIT",
"dependencies": {
"@babel/core": "^7.26.0",
"@babel/plugin-transform-react-jsx-self": "^7.25.9",
"@babel/plugin-transform-react-jsx-source": "^7.25.9",
"@types/babel__core": "^7.20.5",
"react-refresh": "^0.14.2"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"peerDependencies": {
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0"
}
},
"node_modules/@volar/kit": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.4.6.tgz",
@@ -3518,7 +3894,6 @@
"version": "4.24.0",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz",
"integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==",
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -3605,7 +3980,6 @@
"version": "1.0.30001667",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz",
"integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==",
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -4022,6 +4396,12 @@
"node": "^14.18.0 || >=16.10.0"
}
},
"node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"license": "MIT"
},
"node_modules/cookie": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
@@ -4126,6 +4506,12 @@
"integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==",
"license": "MIT"
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT"
},
"node_modules/debug": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
@@ -4341,8 +4727,7 @@
"node_modules/electron-to-chromium": {
"version": "1.5.32",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.32.tgz",
"integrity": "sha512-M+7ph0VGBQqqpTT2YrabjNKSQ2fEl9PVx6AK3N558gDH9NO8O6XN9SXXFWRo9u9PbEg/bWq+tjXQr+eXmxubCw==",
"dev": true
"integrity": "sha512-M+7ph0VGBQqqpTT2YrabjNKSQ2fEl9PVx6AK3N558gDH9NO8O6XN9SXXFWRo9u9PbEg/bWq+tjXQr+eXmxubCw=="
},
"node_modules/emmet": {
"version": "2.4.11",
@@ -4482,7 +4867,6 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"engines": {
"node": ">=6"
}
@@ -5248,6 +5632,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@@ -5985,6 +6378,12 @@
"jiti": "bin/jiti.js"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.0",
"license": "MIT",
@@ -5995,6 +6394,18 @@
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/jsesc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
"license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
},
"engines": {
"node": ">=6"
}
},
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -6013,6 +6424,18 @@
"dev": true,
"license": "MIT"
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"license": "MIT",
"bin": {
"json5": "lib/cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/jsonc-parser": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz",
@@ -7699,8 +8122,7 @@
"node_modules/node-releases": {
"version": "2.0.18",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
"integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
"dev": true
"integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g=="
},
"node_modules/nopt": {
"version": "8.1.0",
@@ -8480,6 +8902,36 @@
"resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz",
"integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="
},
"node_modules/react": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
"license": "MIT",
"dependencies": {
"scheduler": "^0.25.0"
},
"peerDependencies": {
"react": "^19.0.0"
}
},
"node_modules/react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
"integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/read-cache": {
"version": "1.0.0",
"dev": true,
@@ -8976,6 +9428,12 @@
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="
},
"node_modules/scheduler": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
"integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
"license": "MIT"
},
"node_modules/semver": {
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz",
@@ -9592,13 +10050,6 @@
"tlds": "bin.js"
}
},
"node_modules/to-fast-properties": {
"version": "2.0.0",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -10073,7 +10524,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
"integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
"dev": true,
"funding": [
{
"type": "opencollective",

View File

@@ -23,16 +23,21 @@
},
"dependencies": {
"@astrojs/netlify": "^6.1.0",
"@astrojs/react": "^4.2.0",
"@astrojs/rss": "^4.0.11",
"@astrojs/sitemap": "^3.2.1",
"@astrolib/analytics": "^0.6.1",
"@astrolib/seo": "^1.0.0-beta.8",
"@fontsource-variable/inter": "^5.1.1",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"astro": "^5.2.3",
"astro-embed": "^0.9.0",
"astro-icon": "^1.1.5",
"limax": "4.1.0",
"lodash.merge": "^4.6.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"unpic": "^3.22.0"
},
"devDependencies": {
@@ -41,6 +46,7 @@
"@astrojs/partytown": "^2.1.3",
"@astrojs/tailwind": "^6.0.0",
"@eslint/js": "^9.18.0",
"@iconify-json/circle-flags": "^1.2.6",
"@iconify-json/flat-color-icons": "^1.2.1",
"@iconify-json/tabler": "^1.2.14",
"@tailwindcss/typography": "^0.5.16",

View File

@@ -0,0 +1,147 @@
---
// src/components/LanguageDropdown.astro
import { Icon } from 'astro-icon/components';
interface Props {
currentLang: string;
}
const { currentLang } = Astro.props;
const languages = [
{ code: 'en', name: 'English', flag: 'gb' },
{ code: 'nl', name: 'Dutch', flag: 'nl' },
{ code: 'de', name: 'German', flag: 'de' },
];
const currentLanguage = languages.find(lang => lang.code === currentLang) || languages[0];
---
<div class="relative inline-block text-left">
<div>
<button
type="button"
class="inline-flex justify-center w-full rounded-md border border-gray-300 dark:border-gray-600 shadow-sm px-4 py-2 bg-white dark:bg-gray-800 text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 dark:focus:ring-offset-gray-800 focus:ring-indigo-500 dark:focus:ring-indigo-400 transition-colors duration-200"
id="menu-button"
aria-expanded="false"
aria-haspopup="true"
>
<Icon name={`circle-flags:${currentLanguage.flag}`} class="inline-block w-5 h-5 mr-2" />
<span id="selected-language">{currentLanguage.name}</span>
<Icon
name="tabler:chevron-down"
class="ml-2 -mr-1 h-5 w-5 transition-transform duration-200"
aria-hidden="true"
id="chevron-icon"
/>
</button>
</div>
<div
class="hidden origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white dark:bg-gray-800 ring-1 ring-black ring-opacity-5 dark:ring-gray-600 focus:outline-none transform opacity-0 scale-95 transition-all duration-200"
role="menu"
aria-orientation="vertical"
aria-labelledby="menu-button"
tabindex="-1"
id="language-menu"
>
<div class="py-1" role="none">
{languages.map(lang => (
<a
href={`/${lang.code}`}
class="text-gray-700 dark:text-gray-300 block px-4 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white transition-colors duration-200"
role="menuitem"
tabindex="-1"
>
<Icon name={`circle-flags:${lang.flag}`} class="inline-block w-5 h-5 mr-2" />
{lang.name}
</a>
))}
</div>
</div>
</div>
<style>
#language-menu:not(.hidden) {
animation: slideIn 0.2s ease-out forwards;
}
@keyframes slideIn {
from {
opacity: 0;
transform: scale(0.95) translateY(-0.5rem);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
</style>
<script>
function setupLanguageDropdown() {
const button = document.querySelector<HTMLButtonElement>('#menu-button');
const menu = document.querySelector<HTMLDivElement>('#language-menu');
const chevronIcon = document.querySelector<HTMLElement>('#chevron-icon');
if (!button || !menu || !chevronIcon) {
return;
}
let isOpen = false;
function closeMenu() {
if (menu && button && chevronIcon) {
menu.classList.add('hidden');
button.setAttribute('aria-expanded', 'false');
chevronIcon.style.transform = 'rotate(0deg)';
isOpen = false;
}
}
function openMenu() {
if (menu && button && chevronIcon) {
menu.classList.remove('hidden');
button.setAttribute('aria-expanded', 'true');
chevronIcon.style.transform = 'rotate(180deg)';
isOpen = true;
}
}
// Initialize closed state
closeMenu();
// Toggle menu
button.addEventListener('click', (e: MouseEvent) => {
e.stopPropagation();
if (isOpen) {
closeMenu();
} else {
openMenu();
}
});
// Close when clicking outside
document.addEventListener('click', (e: MouseEvent) => {
const target = e.target as HTMLElement;
if (isOpen && !menu.contains(target) && !button.contains(target)) {
closeMenu();
}
});
// Handle keyboard navigation
document.addEventListener('keydown', (e: KeyboardEvent) => {
if (e.key === 'Escape' && isOpen) {
closeMenu();
button.focus();
}
});
}
// Run setup when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', setupLanguageDropdown);
} else {
setupLanguageDropdown();
}
</script>

View File

@@ -1,37 +1,11 @@
---
// Define your supported languages
const languages = [
{ code: 'en', label: 'English' },
{ code: 'fr', label: 'Français' },
{ code: 'es', label: 'Español' },
];
import LanguageSelectorComponent from './LanguageSelectorReact';
// Determine the current language from the URL for setting the selected option.
const pathSegments = window.location.pathname.split('/').filter(Boolean);
const currentLang = pathSegments[0] || 'en';
interface Props {
defaultLang: string;
}
const { defaultLang } = Astro.props;
---
<select id="language-selector" class="p-2 border rounded bg-white text-gray-700">
{languages.map((lang) => (
<option value={lang.code} selected={lang.code === currentLang}>
{lang.label}
</option>
))}
</select>
<script>
// This script runs in the browser
const select = document.getElementById('language-selector');
select.addEventListener('change', (event) => {
const selectedLang = event.target.value;
const currentPath = window.location.pathname;
const segments = currentPath.split('/').filter(Boolean);
const supportedLangs = ['en', 'fr', 'es'];
if (supportedLangs.includes(segments[0])) {
segments[0] = selectedLang;
} else {
segments.unshift(selectedLang);
}
const newPath = '/' + segments.join('/');
window.location.href = newPath;
});
</script>
<LanguageSelectorComponent client:load defaultLang={defaultLang} />

View File

@@ -0,0 +1,61 @@
import { useState } from 'react';
import type { ComponentProps } from 'react';
import { Icon as IconComponent } from 'astro-icon/components';
// Create a wrapper component for Icon with proper TypeScript types
const Icon = ({ className, ...props }: ComponentProps<typeof IconComponent> & { className?: string }) => {
return <IconComponent {...props} class={className} />;
};
interface Language {
code: string;
name: string;
flag: string;
}
const languages: Language[] = [
{ code: 'en', name: 'English', flag: 'gb' },
{ code: 'nl', name: 'Dutch', flag: 'nl' },
{ code: 'de', name: 'German', flag: 'de' },
];
interface LanguageSelectorProps {
defaultLang: string;
}
export default function LanguageSelectorComponent({ defaultLang }: LanguageSelectorProps) {
const [currentLang] = useState<string>(defaultLang);
const handleLanguageSelect = (code: string) => {
if (code !== currentLang) {
window.location.href = `/${code}`;
}
};
return (
<div className="flex items-center space-x-4">
{languages.map((language) => (
<button
key={language.code}
onClick={() => handleLanguageSelect(language.code)}
className={`
inline-flex items-center px-3 py-2 text-sm font-medium rounded-md
transition-colors duration-200 hover:bg-gray-100
${language.code === currentLang
? 'text-blue-600 bg-blue-50'
: 'text-gray-600 hover:text-gray-900'
}
`}
aria-current={language.code === currentLang ? 'page' : undefined}
>
<Icon
name={`circle-flags:${language.flag}`}
className="w-5 h-5 mr-2"
aria-hidden="true"
/>
<span>{language.name}</span>
</button>
))}
</div>
);
}

View File

@@ -4,11 +4,10 @@ import { Icon } from 'astro-icon/components';
import Logo from '~/components/Logo.astro';
import ToggleTheme from '~/components/common/ToggleTheme.astro';
import ToggleMenu from '~/components/common/ToggleMenu.astro';
import Button from '~/components/ui/Button.astro';
import LanguageDropdown from '~/components/LanguageDropdown.astro';
import { getHomePermalink } from '~/utils/permalinks';
import { trimSlash, getAsset } from '~/utils/permalinks';
import type { CallToAction } from '~/types';
interface Link {
text?: string;
@@ -24,7 +23,6 @@ interface MenuLink extends Link {
export interface Props {
id?: string;
links?: Array<MenuLink>;
actions?: Array<CallToAction>;
isSticky?: boolean;
isDark?: boolean;
isFullWidth?: boolean;
@@ -36,7 +34,6 @@ export interface Props {
const {
id = 'header',
links = [],
actions = [],
isSticky = false,
isDark = false,
isFullWidth = false,
@@ -62,7 +59,7 @@ const currentPath = `/${trimSlash(new URL(Astro.url).pathname)}`;
'relative text-default py-3 px-3 md:px-6 mx-auto w-full',
{
'md:flex md:justify-between': position !== 'center',
},
},
{
'md:grid md:grid-cols-3 md:items-center': position === 'center',
},
@@ -150,60 +147,10 @@ const currentPath = `/${trimSlash(new URL(Astro.url).pathname)}`;
</a>
)
}
<!-- Language Selector as Borderless Buttons -->
<div id="language-selector" class="flex space-x-4">
<button
type="button"
data-lang="en"
class="dark:text-white:hover:text-blue-500 focus:outline-none"
>
EN
</button>
<button
type="button"
data-lang="nl"
class="dark:text-white:hover:text-blue-500 focus:outline-none"
>
NL
</button>
<button
type="button"
data-lang="de"
class="dark:text-white:hover:text-blue-500 focus:outline-none"
>
DE
</button>
</div>
<!-- Language Selector as Select Element -->
<LanguageDropdown currentLang={currentPath.split('/')[1] || 'en'} />
</div>
</div>
</div>
</div>
<script client:load>
// Define supported languages
const supportedLangs = ['en', 'nl', 'de'];
// Split current URL path into segments
let segments = window.location.pathname.split('/').filter(Boolean);
// Determine current language based on URL, defaulting to 'en'
const currentLang = supportedLangs.includes(segments[0]) ? segments[0] : 'en';
// Add active styling and event listeners to language buttons
document.querySelectorAll('#language-selector button').forEach((button) => {
if (button.getAttribute('data-lang') === currentLang) {
button.classList.add('font-bold', 'text-blue-500');
}
button.addEventListener('click', () => {
// Re-read the URL segments in case the path has changed
segments = window.location.pathname.split('/').filter(Boolean);
const selectedLang = button.getAttribute('data-lang');
if (supportedLangs.includes(segments[0])) {
segments[0] = selectedLang;
} else {
segments.unshift(selectedLang);
}
// Rebuild the URL and redirect
const newPath = '/' + segments.join('/');
window.location.href = newPath;
});
});
</script>
</header>

View File

@@ -83,9 +83,7 @@ Once published, the extension will be available directly from the Chrome Web Sto
🔹 **Advanced AI-based phishing detection** More intelligent scanning for phishing patterns.
🔹 **Expanded enterprise app coverage** Supporting even more cloud-based applications.
If you have feature requests or want to contribute, check out our **GitHub repository**:
👉 **[View the Source Code on GitHub](https://github.com/rrpbergsma/EnterpriseAppProtection)**
If you have feature requests or want to contribute, check out our **GitHub repository**: 👉 **[View the Source Code on GitHub](https://github.com/rrpbergsma/EnterpriseAppProtection)**
---
@@ -106,4 +104,4 @@ Enterprise App Protection is your **first line of defense against phishing attac
---
Would love to hear your feedback! Let me know what you think. 🚀
Would love to hear your feedback! Let me know what you think. 🚀

View File

@@ -114,4 +114,4 @@ For organizations looking to enhance productivity, reduce IT costs, and improve
---
If you're interested in learning more about Nexthink or exploring how it can transform your IT operations, feel free to reach out or request a demo from Nexthinks official website.
If you're interested in learning more about Nexthink or exploring how it can transform your IT operations, feel free to reach out or request a demo from [Nexthinks official website](https://www.nexthink.com) rel="noopener noreferrer".

View File

@@ -8,7 +8,6 @@ import Features3 from '~/components/widgets/Features3.astro';
import Testimonials from '~/components/widgets/Testimonials.astro';
import Steps from '~/components/widgets/Steps.astro';
import BlogLatestPosts from '~/components/widgets/BlogLatestPosts.astro';
import { getPermalink } from '~/utils/permalinks';
import HomePageImage from '~/assets/images/richardbergsma.png'
import MicrosoftAssociate from '~/assets/images/microsoft-certified-associate-badge.webp'
import NexthinkAssociate from '~/assets/images/NexthinkAssociate.webp'
@@ -17,14 +16,10 @@ import pcep from '~/assets/images/PCEP.webp'
import MicrosoftFundamentals from '~/assets/images/microsoft-certified-fundamentals-badge.webp'
import NexthinkAppExp from '~/assets/images/CertifiedNexthinkProfessionalinApplicationExperienceManagement.webp'
const metadata = {
title: 'Über mich',
};
const fixedYear = 2010; // Ersetze dies durch das gewünschte Jahr
const currentYear = new Date().getFullYear();
const yearsSince = currentYear - fixedYear;
---
<Layout metadata={metadata}>
@@ -35,21 +30,33 @@ const yearsSince = currentYear - fixedYear;
{ text: 'Startseite', href: '#hero' },
{ text: 'Über mich', href: '#about' },
{ text: 'Lebenslauf', href: '#resume' },
{ text: 'Zertifizierungen', href: '#Zertifizierungen' },
{ text: 'Zertifizierungen', href: '#Certifications' },
{ text: 'Fähigkeiten', href: '#skills' },
{ text: 'Blog', href: '#blog' },
]}
actions={[
{
text: 'Kontaktiere mich',
href: getPermalink('/contact#form'),
},
]}
isSticky
showToggleTheme
/>
</Fragment>
<script>
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const targetId = this.getAttribute('href').substring(1);
const targetElement = document.getElementById(targetId);
if (targetElement) {
window.scrollTo({
top: targetElement.offsetTop - 50, // Adjust offset as needed
behavior: 'smooth'
});
}
});
});
</script>
<!-- Hero Widget ******************* -->
<Hero
@@ -57,8 +64,7 @@ const yearsSince = currentYear - fixedYear;
title="Systeme vereinfachen, Ergebnisse verstärken"
>
<Fragment slot="subtitle">
<strong class="text-4xl">Hallo! Ich bin Richard Bergsma.</strong></br></br>
Ich automatisiere Arbeitsabläufe mit Power Automate, entwickle intelligente Chatbots im Copilot Studio und verbinde Systeme durch nahtlose API-Integrationen. Von der Optimierung der IT-Infrastruktur und dem Management globaler Umgebungen bis hin zur Verbesserung der Zusammenarbeit mit SharePoint und Azure ich optimiere Prozesse, damit Technologie intelligenter arbeitet, und verbessere dabei kontinuierlich meine Python-Kenntnisse.
<strong class="text-3xl md:text-4xl">Hallo! Ich bin Richard Bergsma.</strong><br /><br />Ich automatisiere Arbeitsabläufe mit Power Automate, entwickle intelligente Chatbots im Copilot Studio und verbinde Systeme durch nahtlose API-Integrationen. Von der Optimierung der IT-Infrastruktur und dem Management globaler Umgebungen bis hin zur Verbesserung der Zusammenarbeit mit SharePoint und Azure ich optimiere Prozesse, damit Technologie intelligenter arbeitet, und verbessere dabei kontinuierlich meine Python-Kenntnisse.
</Fragment>
</Hero>
@@ -75,14 +81,12 @@ const yearsSince = currentYear - fixedYear;
}}
>
<Fragment slot="content">
<h2 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">Über mich</h2>
Mit über 15 Jahren IT-Erfahrung bin ich ein leidenschaftlicher IT-System- und Automatisierungsmanager, der optimale Lösungen für komplexe Cloud- und On-Premise-Systeme entwickelt. Ich konzentriere mich darauf, Automatisierung mit Power Automate voranzutreiben, intelligente Chatbots im Copilot Studio zu bauen und APIs zu integrieren, um Arbeitsabläufe zu optimieren. Zudem verwalte ich die Microsoft 365-Umgebung, unterstütze Anfragen der dritten Ebene und steigere die Effizienz mit Tools wie Power Apps, Nexthink und TOPdesk.
</br></br>
Früher leitete ich Implementierungen von Microsoft 365 und SharePoint Online, migrierte E-Mail-Systeme und verbesserte die Automatisierung mit SCCM. Darüber hinaus gründete ich Bergsma IT, um kleinen Unternehmen beim Umstieg in die Cloud zu helfen und individuelle WordPress-Websites zu betreuen.
</br></br>
Ich besitze Zertifizierungen in Microsoft Teams Administration, Azure Fundamentals und Nexthink Administration. Meine Mission ist es, IT-Exzellenz voranzutreiben, indem ich Cloud-Lösungen optimiere, Prozesse automatisiere und herausragenden technischen Support leiste.
<h2 class="text-3xl font-bold tracking-tight dark:text-white sm:text-4xl mb-2">Über mich</h2>
Mit über 15 Jahren IT-Erfahrung bin ich ein leidenschaftlicher IT-System- und Automatisierungsmanager, der optimale Lösungen für komplexe Cloud- und On-Premise-Systeme entwickelt. Ich konzentriere mich darauf, Automatisierung mit Power Automate voranzutreiben, intelligente Chatbots im Copilot Studio zu bauen und APIs zu integrieren, um Arbeitsabläufe zu optimieren. Zudem verwalte ich die Microsoft 365-Umgebung, unterstütze Anfragen der dritten Ebene und steigere die Effizienz mit Tools wie Power Apps, Nexthink und TOPdesk.
<br /><br />Früher leitete ich Implementierungen von Microsoft 365 und SharePoint Online, migrierte E-Mail-Systeme und verbesserte die Automatisierung mit SCCM. Darüber hinaus gründete ich Bergsma IT, um kleinen Unternehmen beim Umstieg in die Cloud zu helfen und individuelle WordPress-Websites zu betreuen.
<br /><br />Ich besitze Zertifizierungen in Microsoft Teams Administration, Azure Fundamentals und Nexthink Administration. Meine Mission ist es, IT-Exzellenz voranzutreiben, indem ich Cloud-Lösungen optimiere, Prozesse automatisiere und herausragenden technischen Support leiste.
</Fragment>
<Fragment slot="bg">
@@ -139,7 +143,7 @@ const yearsSince = currentYear - fixedYear;
<!-- Steps Widget: Ausbildung ****************** -->
<Steps
id="Ausbildung"
id="Education"
title="Ausbildung"
items={[
{
@@ -161,7 +165,7 @@ const yearsSince = currentYear - fixedYear;
<!-- Testimonials Widget: Zertifizierungen *********** -->
<Testimonials
id="Zertifizierungen"
id="Certifications"
title="Zertifizierungen"
subtitle="Wo Wissen auf Anerkennung trifft"
testimonials={[

View File

@@ -8,7 +8,6 @@ import Features3 from '~/components/widgets/Features3.astro';
import Testimonials from '~/components/widgets/Testimonials.astro';
import Steps from '~/components/widgets/Steps.astro';
import BlogLatestPosts from '~/components/widgets/BlogLatestPosts.astro';
import { getPermalink } from '~/utils/permalinks';
import HomePageImage from '~/assets/images/richardbergsma.png'
import MicrosoftAssociate from '~/assets/images/microsoft-certified-associate-badge.webp'
import NexthinkAssociate from '~/assets/images/NexthinkAssociate.webp'
@@ -21,9 +20,6 @@ const metadata = {
title: 'About me',
};
const fixedYear = 2010; // Replace with your desired year
const currentYear = new Date().getFullYear();
const yearsSince = currentYear - fixedYear;
---
<Layout metadata={metadata}>
@@ -38,26 +34,37 @@ const yearsSince = currentYear - fixedYear;
{ text: 'Skills', href: '#skills' },
{ text: 'Blog', href: '#blog' },
]}
actions={[
{
text: 'Contact me',
href: getPermalink('/contact#form'),
},
]}
isSticky
showToggleTheme
/>
</Fragment>
<script>
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const targetId = this.getAttribute('href').substring(1);
const targetElement = document.getElementById(targetId);
if (targetElement) {
window.scrollTo({
top: targetElement.offsetTop - 50, // Adjust offset as needed
behavior: 'smooth'
});
}
});
});
</script>
<!-- Hero2 Widget ******************* -->
<Hero
id="hero"
title="Simplifying Systems, Amplifying Results"
>
title="Simplifying Systems, Amplifying Results">
<Fragment slot="subtitle">
<strong class="text-4xl">Hi! I am Richard Bergsma.</strong></br></br>I automate workflows with Power Automate, build smart chatbots in Copilot Studio, and connect systems through seamless API integrations. From optimizing IT infrastructure and managing global environments to enhancing collaboration with SharePoint and Azure, I streamline processes to make technology work smarter—all while leveling up my Python skills.
<strong class="text-3xl md:text-4xl">Hi! I am Richard Bergsma.</strong><br /><br />I automate workflows with Power Automate, build smart chatbots in Copilot Studio, and connect systems through seamless API integrations. From optimizing IT infrastructure and managing global environments to enhancing collaboration with SharePoint and Azure, I streamline processes to make technology work smarter—all while leveling up my Python skills.
</Fragment>
</Hero>
@@ -77,12 +84,12 @@ const yearsSince = currentYear - fixedYear;
}}
>
<Fragment slot="content">
<h2 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">About me</h2>
<h2 class="text-3xl font-bold tracking-tight dark:text-white sm:text-4xl mb-2">About me</h2>
With over 15 years of IT experience, I am a passionate IT Systems and Automation Manager who thrives on delivering optimal solutions for complex cloud and on-premise systems. I focus on driving automation with Power Automate, building intelligent chatbots in Copilot Studio, and integrating APIs to streamline workflows. I also manage the Microsoft 365 environment, support 3rd line requests, and enhance efficiency with tools like Power Apps, Nexthink, and TOPdesk.
</br></br>Previously, I led Microsoft 365 and SharePoint Online implementations, migrated mail systems, and improved automation with SCCM. Additionally, I founded Bergsma IT, helping small businesses move to the cloud and managing tailored WordPress websites.
<br /><br />Previously, I led Microsoft 365 and SharePoint Online implementations, migrated mail systems, and improved automation with SCCM. Additionally, I founded Bergsma IT, helping small businesses move to the cloud and managing tailored WordPress websites.
</br></br>I hold certifications in Microsoft Teams Administration, Azure Fundamentals, and Nexthink Administration. My mission is to drive IT excellence by optimizing cloud solutions, automating processes, and providing outstanding technical support.
<br /><br />I hold certifications in Microsoft Teams Administration, Azure Fundamentals, and Nexthink Administration. My mission is to drive IT excellence by optimizing cloud solutions, automating processes, and providing outstanding technical support.
</Fragment>
<Fragment slot="bg">
@@ -251,7 +258,7 @@ const yearsSince = currentYear - fixedYear;
linkUrl: 'https://learn.microsoft.com/en-us/credentials/certifications/azure-fundamentals/?practice-assessment-type=certification',
image: {
src: MicrosoftFundamentals,
alt: 'Azure Fundamentals badge',
alt: 'Microsoft Certified Associate badge',
loading: 'lazy',
},
},
@@ -424,7 +431,7 @@ const yearsSince = currentYear - fixedYear;
</Fragment>
</Content>
-->
-->
<!-- BlogLatestPost Widget **************** -->

View File

@@ -8,7 +8,6 @@ import Features3 from '~/components/widgets/Features3.astro';
import Testimonials from '~/components/widgets/Testimonials.astro';
import Steps from '~/components/widgets/Steps.astro';
import BlogLatestPosts from '~/components/widgets/BlogLatestPosts.astro';
import { getPermalink } from '~/utils/permalinks';
import HomePageImage from '~/assets/images/richardbergsma.png'
import MicrosoftAssociate from '~/assets/images/microsoft-certified-associate-badge.webp'
import NexthinkAssociate from '~/assets/images/NexthinkAssociate.webp'
@@ -17,14 +16,10 @@ import pcep from '~/assets/images/PCEP.webp'
import MicrosoftFundamentals from '~/assets/images/microsoft-certified-fundamentals-badge.webp'
import NexthinkAppExp from '~/assets/images/CertifiedNexthinkProfessionalinApplicationExperienceManagement.webp'
const metadata = {
title: 'Over mij',
};
const fixedYear = 2010; // Vervang door het gewenste jaar
const currentYear = new Date().getFullYear();
const yearsSince = currentYear - fixedYear;
---
<Layout metadata={metadata}>
@@ -35,33 +30,44 @@ const yearsSince = currentYear - fixedYear;
{ text: 'Home', href: '#hero' },
{ text: 'Over', href: '#about' },
{ text: 'CV', href: '#resume' },
{ text: 'Certificeringen', href: '#Certificeringen' },
{ text: 'Certificeringen', href: '#Certifications' },
{ text: 'Vaardigheden', href: '#skills' },
{ text: 'Blog', href: '#blog' },
]}
actions={[
{
text: 'Contacteer mij',
href: getPermalink('/contact#form'),
},
]}
isSticky
showToggleTheme
/>
</Fragment>
<script>
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const targetId = this.getAttribute('href').substring(1);
const targetElement = document.getElementById(targetId);
if (targetElement) {
window.scrollTo({
top: targetElement.offsetTop - 50, // Adjust offset as needed
behavior: 'smooth'
});
}
});
});
</script>
<!-- Hero2 Widget ******************* -->
<Hero
id="hero"
title="Systemen vereenvoudigen, Resultaten versterken"
>
<Fragment slot="subtitle">
<strong class="text-4xl">Hoi! Ik ben Richard Bergsma.</strong></br></br>
Ik automatiseer werkstromen met Power Automate, bouw slimme chatbots in Copilot Studio en verbind systemen via naadloze API-integraties. Van het optimaliseren van IT-infrastructuur en het beheren van wereldwijde omgevingen tot het verbeteren van samenwerking met SharePoint en Azure, stroomlijn ik processen om technologie slimmer te laten werken terwijl ik mijn Python-vaardigheden verder ontwikkel.
<strong class="text-3xl md:text-4xl">Hoi! Ik ben Richard Bergsma.</strong><br /><br />Ik automatiseer werkstromen met Power Automate, bouw slimme chatbots in Copilot Studio en verbind systemen via naadloze API-integraties. Van het optimaliseren van IT-infrastructuur en het beheren van wereldwijde omgevingen tot het verbeteren van samenwerking met SharePoint en Azure, stroomlijn ik processen om technologie slimmer te laten werken terwijl ik mijn Python-vaardigheden verder ontwikkel.
</Fragment>
</Hero>
<!-- Content Widget **************** -->
@@ -69,9 +75,7 @@ const yearsSince = currentYear - fixedYear;
<Content
id="about"
columns={2}
items={[
]}
items={[]}
image={{
src: HomePageImage,
alt: 'Richard Bergsma die glimlacht in de bergen van Zwitserland terwijl hij Revella vasthoudt',
@@ -79,12 +83,12 @@ const yearsSince = currentYear - fixedYear;
}}
>
<Fragment slot="content">
<h2 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">Over mij</h2>
<h2 class="text-3xl font-bold tracking-tight dark:text-white sm:text-4xl mb-2">Over mij</h2>
Met meer dan 15 jaar IT-ervaring ben ik een gepassioneerde IT-systemen- en automatiseringsmanager die uitblinkt in het leveren van optimale oplossingen voor complexe cloud- en on-premise systemen. Ik richt mij op het stimuleren van automatisering met Power Automate, het bouwen van intelligente chatbots in Copilot Studio en het integreren van API's om werkstromen te stroomlijnen. Ik beheer ook de Microsoft 365-omgeving, ondersteun derde-lijnsaanvragen en verhoog de efficiëntie met tools zoals Power Apps, Nexthink en TOPdesk.
</br></br>Vroeger leidde ik de implementaties van Microsoft 365 en SharePoint Online, migreerde ik mailsystemen en verbeterde ik de automatisering met SCCM. Daarnaast richtte ik Bergsma IT op, waarmee ik kleine bedrijven hielp de overstap naar de cloud te maken en beheer ik op maat gemaakte WordPress-websites.
<br /><br />Vroeger leidde ik de implementaties van Microsoft 365 en SharePoint Online, migreerde ik mailsystemen en verbeterde ik de automatisering met SCCM. Daarnaast richtte ik Bergsma IT op, waarmee ik kleine bedrijven hielp de overstap naar de cloud te maken en beheer ik op maat gemaakte WordPress-websites.
</br></br>Ik ben gecertificeerd in Microsoft Teams Administration, Azure Fundamentals en Nexthink Administration. Mijn missie is IT-excellentie te bevorderen door het optimaliseren van cloudoplossingen, het automatiseren van processen en het leveren van uitstekende technische ondersteuning.
<br /><br />Ik ben gecertificeerd in Microsoft Teams Administration, Azure Fundamentals en Nexthink Administration. Mijn missie is IT-excellentie te bevorderen door het optimaliseren van cloudoplossingen, het automatiseren van processen en het leveren van uitstekende technische ondersteuning.
</Fragment>
<Fragment slot="bg">