mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-22 12:03:25 -04:00

the #shared alias used by Nuxt cannot be easily disabled and to prevent breackage with jiti, we make use of it
85 lines
2.5 KiB
Vue
85 lines
2.5 KiB
Vue
<script setup lang="ts">
|
|
import { useRoute, useNuxtApp } from '#app';
|
|
import { clearUrl } from '#shared/helpers.ts';
|
|
import useConfig from '~/composables/useConfig.ts';
|
|
import useSimpleHead from '~/composables/useSimpleHead.ts';
|
|
import { getUrlForLocale } from '~/src/domain.ts';
|
|
|
|
const { $locales: locales, $t, $translator: translator } = useNuxtApp();
|
|
const config = useConfig();
|
|
|
|
const route = useRoute();
|
|
|
|
useSimpleHead({
|
|
title: translator.translate('macrolanguage.title'),
|
|
description: translator.translate('macrolanguage.prompt'),
|
|
}, translator);
|
|
|
|
definePageMeta({
|
|
translatedPaths: (config) => {
|
|
if (!config.macrolanguage?.enabled) {
|
|
return [];
|
|
}
|
|
return ['/langswitch'];
|
|
},
|
|
});
|
|
|
|
const getSanitizedReturnPath = (): string => {
|
|
const returnPath = route.query.return;
|
|
|
|
if (!returnPath || typeof returnPath !== 'string' || returnPath.length > 128) {
|
|
console.warn('Invalid return path:', returnPath);
|
|
return '/';
|
|
}
|
|
|
|
// Validate the return path:
|
|
// - must start with /
|
|
// - must only match Unicode word characters, at-signs, hyphens, slashes, dots, colons, and underscores
|
|
// - must not have any ".." segments
|
|
|
|
const segments = returnPath.split('/').filter(Boolean);
|
|
if (segments.some((segment) => !/^[\w\-/:.@]+$/u.test(segment) || segment === '..')) {
|
|
return '/';
|
|
}
|
|
|
|
return `/${segments.join('/')}`;
|
|
};
|
|
|
|
const returnPath = getSanitizedReturnPath();
|
|
|
|
const options = (config.macrolanguage?.languages || []).map(({ code }) => ({
|
|
key: code,
|
|
name: locales[code]?.name,
|
|
extra: locales[code]?.extra,
|
|
url: `${getUrlForLocale(code)}${returnPath}`,
|
|
domain: clearUrl(getUrlForLocale(code)),
|
|
}));
|
|
</script>
|
|
|
|
<template>
|
|
<div v-if="config.macrolanguage?.enabled" class="container">
|
|
<div class="mb-4 d-none d-lg-block">
|
|
|
|
</div>
|
|
<p class="mb-3 fs-5">
|
|
{{ $t('macrolanguage.prompt') }}
|
|
</p>
|
|
<div class="list-group">
|
|
<a
|
|
v-for="option in options"
|
|
:key="option.key"
|
|
:href="option.url"
|
|
class="list-group-item list-group-item-action"
|
|
>
|
|
<div class="d-flex w-100 justify-content-between">
|
|
<h5 class="mb-1">
|
|
{{ option.name }}
|
|
<small v-if="option.extra">({{ option.extra }})</small>
|
|
</h5>
|
|
</div>
|
|
<p class="mb-1">{{ option.domain }}</p>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</template>
|