diff --git a/components/CalendarBanner.vue b/components/CalendarBanner.vue
index f340b58f1..c6fe894a8 100644
--- a/components/CalendarBanner.vue
+++ b/components/CalendarBanner.vue
@@ -1,3 +1,21 @@
+
+
-
-
diff --git a/components/Logo.vue b/components/Logo.vue
index eba9aa0a6..2ee9fcf57 100644
--- a/components/Logo.vue
+++ b/components/Logo.vue
@@ -25,8 +25,8 @@ import { storeToRefs } from 'pinia';
import { defineComponent } from 'vue';
import logoSvg from '../public/logo/logo.svg?raw';
-import { buildCalendar } from '../src/calendar/calendar.ts';
import { Day } from '../src/calendar/helpers.ts';
+import { loadCalendar } from '../src/data.ts';
import { ImmutableArray } from '../src/helpers.ts';
import { useMainStore } from '../store/index.ts';
@@ -47,8 +47,7 @@ export default defineComponent({
day: { default: () => Day.today(), type: Day },
},
async setup() {
- const runtimeConfig = useRuntimeConfig();
-
+ const calendarPromise = loadCalendar();
const { data: stats } = await useFetch<{ overall: { users: number } }>(
'/api/admin/stats-public',
{ lazy: true },
@@ -57,7 +56,7 @@ export default defineComponent({
return {
stats,
selectedDay: storeToRefs(useMainStore()).selectedDay,
- calendar: buildCalendar(runtimeConfig.public.baseUrl),
+ calendar: await calendarPromise,
};
},
data(): Data {
diff --git a/components/Profile.vue b/components/Profile.vue
index 89e1ebd56..526ea2b4b 100644
--- a/components/Profile.vue
+++ b/components/Profile.vue
@@ -4,9 +4,8 @@ import { useNuxtApp } from 'nuxt/app';
import useConfig from '~/composables/useConfig.ts';
import useMainPronoun from '~/composables/useMainPronoun.ts';
import useSpelling from '~/composables/useSpelling.ts';
-import { buildCalendar } from '~/src/calendar/calendar.ts';
import type { TermsEntryRaw } from '~/src/classes.ts';
-import { loadPronounLibrary } from '~/src/data.ts';
+import { loadCalendar, loadPronounLibrary } from '~/src/data.ts';
import { buildFlags } from '~/src/flags.ts';
import { buildImageUrl } from '~/src/helpers.ts';
import type { Profile, UserWithProfiles } from '~/src/profile.ts';
@@ -24,7 +23,6 @@ const props = withDefaults(defineProps<{
const { $translator: translator } = useNuxtApp();
const { convertName } = useSpelling();
const config = useConfig();
-const runtimeConfig = useRuntimeConfig();
const pronounLibrary = await loadPronounLibrary(config);
@@ -40,7 +38,7 @@ const normaliseUrl = (url: string): string | null => {
const allFlags = buildFlags(config.locale);
const minAge = config.ageLimit || 13;
-const year = buildCalendar(runtimeConfig.public.baseUrl).getCurrentYear();
+const year = (await loadCalendar()).getCurrentYear();
const countFlags = computed(() => {
return (props.profile.flags?.length ?? 0) + (props.profile.customFlags?.length ?? 0);
diff --git a/components/TermsDictionary.vue b/components/TermsDictionary.vue
index 36ee91708..7170b6aef 100644
--- a/components/TermsDictionary.vue
+++ b/components/TermsDictionary.vue
@@ -3,9 +3,9 @@ import type { ComponentExposed } from 'vue-component-type-helpers';
import type Table from '~/components/Table.vue';
import type TermsSubmitForm from '~/components/TermsSubmitForm.vue';
-import { buildCalendar } from '~/src/calendar/calendar.ts';
import { TermsEntry } from '~/src/classes.ts';
import type { TermsEntryRaw } from '~/src/classes.ts';
+import { loadCalendar } from '~/src/data.ts';
import { buildDict, clearLinkedText } from '~/src/helpers.ts';
const props = defineProps<{
@@ -13,7 +13,6 @@ const props = defineProps<{
}>();
const { $translator: translator } = useNuxtApp();
-const runtimeConfig = useRuntimeConfig();
const config = useConfig();
const filter = useFilterWithCategory();
@@ -108,7 +107,7 @@ const remove = async (entry: TermsEntry): Promise => {
delete entries.value[entry.id];
};
-const year = buildCalendar(runtimeConfig.public.baseUrl).getCurrentYear()!;
+const year = (await loadCalendar()).getCurrentYear()!;
diff --git a/nuxt.config.ts b/nuxt.config.ts
index 88ef533ce..551bbb8dd 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -240,9 +240,18 @@ export default defineNuxtConfig({
}
},
async 'nitro:config'(nitroConfig) {
- if (nitroConfig.publicAssets === undefined) {
- nitroConfig.publicAssets = [];
- }
+ nitroConfig.virtual ??= {};
+ // workaround to have a dynamic import with variables which currently does not work in Rollup
+ // https://github.com/rollup/rollup/issues/2097
+ nitroConfig.virtual['#virtual/calendar/events.ts'] = () => {
+ const imports = localeDescriptions.map((locale) => {
+ return `${locale.code}: async () =>
+ (await import('~/locale/${locale.code}/calendar/events.ts')).default`;
+ });
+ return `export default {${imports.join(', ')}};`;
+ };
+
+ nitroConfig.publicAssets ??= [];
nitroConfig.publicAssets.push(...(await Promise.all(codes.map(async (code) => [{
baseURL: `/docs/${code}`,
dir: await resolvePath(`~/locale/${code}/docs`),
diff --git a/pages/calendar/[[year]].vue b/pages/calendar/[[year]].vue
index 555593cd5..3859daa8a 100644
--- a/pages/calendar/[[year]].vue
+++ b/pages/calendar/[[year]].vue
@@ -4,8 +4,8 @@ import { useNuxtApp, useRoute } from 'nuxt/app';
import Page from '~/components/Page.vue';
import useConfig from '~/composables/useConfig.ts';
import useSimpleHead from '~/composables/useSimpleHead.ts';
-import { buildCalendar } from '~/src/calendar/calendar.ts';
import { Day } from '~/src/calendar/helpers.ts';
+import { loadCalendar } from '~/src/data.ts';
definePageMeta({
name: 'calendar',
@@ -19,11 +19,10 @@ definePageMeta({
});
const { $translator: translator } = useNuxtApp();
-const runtimeConfig = useRuntimeConfig();
const route = useRoute();
const config = useConfig();
-const calendar = buildCalendar(runtimeConfig.public.baseUrl);
+const calendar = await loadCalendar();
const year = route.params.year
? calendar.getYear(parseInt(route.params.year as string))
: calendar.getCurrentYear();
diff --git a/pages/calendar/[year]-[month]-[day].vue b/pages/calendar/[year]-[month]-[day].vue
index 9d58a35fa..22ba6f07d 100644
--- a/pages/calendar/[year]-[month]-[day].vue
+++ b/pages/calendar/[year]-[month]-[day].vue
@@ -4,8 +4,8 @@ import { useNuxtApp, useRoute } from 'nuxt/app';
import Page from '~/components/Page.vue';
import useConfig from '~/composables/useConfig.ts';
import useSimpleHead from '~/composables/useSimpleHead.ts';
-import { buildCalendar } from '~/src/calendar/calendar.ts';
import { Day } from '~/src/calendar/helpers.ts';
+import { loadCalendar } from '~/src/data.ts';
definePageMeta({
name: 'calendarDay',
@@ -19,7 +19,6 @@ definePageMeta({
});
const { $translator: translator } = useNuxtApp();
-const runtimeConfig = useRuntimeConfig();
const route = useRoute();
const day = new Day(
@@ -34,7 +33,7 @@ useSimpleHead({
}, translator);
const config = useConfig();
-const year = buildCalendar(runtimeConfig.public.baseUrl).getYear(day.year);
+const year = (await loadCalendar()).getYear(day.year);
const basic = route.query.layout === 'basic';
diff --git a/pages/profile/editor.vue b/pages/profile/editor.vue
index 52df9af03..965b5dcd2 100644
--- a/pages/profile/editor.vue
+++ b/pages/profile/editor.vue
@@ -11,8 +11,7 @@ import useSimpleHead from '~/composables/useSimpleHead.ts';
import type { Config } from '~/locale/config.ts';
import { birthdateRange, formatDate, parseDate } from '~/src/birthdate.ts';
import { buildPronounUsage } from '~/src/buildPronoun.ts';
-import { buildCalendar } from '~/src/calendar/calendar.ts';
-import { loadPronounLibrary } from '~/src/data.ts';
+import { loadCalendar, loadPronounLibrary } from '~/src/data.ts';
import { buildList, isValidLink } from '~/src/helpers.ts';
import { addPronounInjectionKey } from '~/src/injectionKeys.ts';
import opinions from '~/src/opinions.ts';
@@ -224,7 +223,7 @@ const visibilityIcons = {
} as Record;
const runtimeConfig = useRuntimeConfig();
-const year = buildCalendar(runtimeConfig.public.baseUrl).getCurrentYear();
+const year = (await loadCalendar()).getCurrentYear();
const { mainPronoun } = useMainPronoun(pronounLibrary, formData, translator);
diff --git a/server/api/search.get.ts b/server/api/search.get.ts
index aa5dba8c3..ea30683c0 100644
--- a/server/api/search.get.ts
+++ b/server/api/search.get.ts
@@ -7,7 +7,7 @@ import type { MatchInfo, SearchResult, AsPlainObject, Options } from 'minisearch
import type { Config } from '~/locale/config.ts';
import { getPosts } from '~/server/blog.ts';
-import { pronounLibrary, pronouns } from '~/server/data.ts';
+import { loadCalendar, pronounLibrary, pronouns } from '~/server/data.ts';
import { getInclusiveEntries } from '~/server/express/inclusive.ts';
import { getNounEntries } from '~/server/express/nouns.ts';
import { getSourcesEntries } from '~/server/express/sources.ts';
@@ -15,7 +15,6 @@ import { getTermsEntries } from '~/server/express/terms.ts';
import { loadSuml, loadSumlFromBase } from '~/server/loader.ts';
import { rootDir } from '~/server/paths.ts';
import { shortForVariant } from '~/src/buildPronoun.ts';
-import { buildCalendar } from '~/src/calendar/calendar.ts';
import { Day } from '~/src/calendar/helpers.ts';
import forbidden from '~/src/forbidden.ts';
import { clearLinkedText, buildImageUrl } from '~/src/helpers.ts';
@@ -810,9 +809,7 @@ const kinds: SearchKind[] = [
}
const base = config.calendar?.route;
- const runtimeConfig = useRuntimeConfig();
-
- const calendar = buildCalendar(runtimeConfig.public.baseUrl);
+ const calendar = await loadCalendar();
const year = Day.today().year;
return (calendar.getYear(year)?.events ?? [])
diff --git a/server/calendar.ts b/server/calendar.ts
index 5f8dee382..3213f4bdd 100644
--- a/server/calendar.ts
+++ b/server/calendar.ts
@@ -5,11 +5,11 @@ import fs from 'fs';
import Pageres from 'pageres';
import type { Config } from '../locale/config.ts';
-import { buildCalendar } from '../src/calendar/calendar.ts';
import dbConnection from './db.ts';
import { loadSuml } from '~/server/loader.ts';
+import { buildCalendar } from '~/src/calendar/calendar.ts';
const __dirname = new URL('.', import.meta.url).pathname;
@@ -60,7 +60,8 @@ const dumpNameDays = async (): Promise => {
const prevPath = `${__dirname}/../cache/calendar.json`;
const prev = fs.existsSync(prevPath) ? JSON.parse(fs.readFileSync(prevPath, 'utf-8')) : {};
- const current = buildCalendar(process.env.BASE_URL!).buildSummary();
+ const localEvents = (await import(`../locale/${config.locale}/calendar/events.ts`)).default;
+ const current = buildCalendar(localEvents, process.env.BASE_URL!).buildSummary();
const changedYears = new Set();
for (const day in current) {
if (!Object.hasOwn(current, day)) {
diff --git a/server/data.ts b/server/data.ts
index 1bb54452a..8e1f650c1 100644
--- a/server/data.ts
+++ b/server/data.ts
@@ -1,6 +1,9 @@
+import localeEventImports from '#virtual/calendar/events.ts';
import type { PronounExamplesData } from '~/locale/data.ts';
import { rootDir } from '~/server/paths.ts';
import { parsePronounGroups, parsePronouns } from '~/src/buildPronoun.ts';
+import { buildCalendar } from '~/src/calendar/calendar.ts';
+import type { Calendar } from '~/src/calendar/helpers.ts';
import { PronounLibrary } from '~/src/classes.ts';
import { loadTsv } from '~/src/tsv.ts';
@@ -10,3 +13,14 @@ const pronounGroups = parsePronounGroups(loadTsv(`${dataDir}/pronouns/pronounGro
export const pronounLibrary = new PronounLibrary(global.config, pronounGroups, pronouns);
export const pronounExamples = loadTsv(`${dataDir}/pronouns/examples.tsv`) as PronounExamplesData[];
+
+let calendar: Calendar | null = null;
+
+export const loadCalendar = async () => {
+ if (calendar === null) {
+ const runtimeConfig = useRuntimeConfig();
+ const localEvents = await localeEventImports[config.locale]();
+ calendar = buildCalendar(localEvents, runtimeConfig.public.baseUrl);
+ }
+ return calendar;
+};
diff --git a/server/express/calendar.ts b/server/express/calendar.ts
index 97aa769ed..d35675df4 100644
--- a/server/express/calendar.ts
+++ b/server/express/calendar.ts
@@ -6,7 +6,6 @@ import SQL from 'sql-template-strings';
import type { LocaleDescription } from '../../locale/locales.ts';
import type { Translations } from '../../locale/translations.ts';
-import { buildCalendar } from '../../src/calendar/calendar.ts';
import { Calendar, Day } from '../../src/calendar/helpers.ts';
import type { Event } from '../../src/calendar/helpers.ts';
import { handleErrorAsync, clearLinkedText } from '../../src/helpers.ts';
@@ -15,13 +14,13 @@ import { loadSuml, loadSumlFromBase } from '../loader.ts';
import { normalise } from './user.ts';
+import { loadCalendar } from '~/server/data.ts';
+
// TODO caching? // import { caches } from "../../src/cache";
const translations = loadSuml('translations') as Translations;
const fallbackTranslations = loadSumlFromBase('locale/_base/translations') as Translations;
-const calendar = buildCalendar(process.env.BASE_URL!);
-
const renderEvents = (yearEvents: Record, res: Response, onlyFirstDays: boolean = false, calNameExtra: string = '') => {
const events = [];
let i = 1;
@@ -79,7 +78,8 @@ const buildMessage = (events: string[], locale: LocaleDescription, day: Day, lin
return message;
};
-const eventsSummary = (day: Day, locale: LocaleDescription) => {
+const eventsSummary = async (day: Day, locale: LocaleDescription) => {
+ const calendar = await loadCalendar();
const eventsRaw = calendar.getCurrentYear()!.eventsByDate[day.toString()];
const link = `${locale.url}/${encodeURIComponent(config.calendar!.route!)}/${day}`;
@@ -126,14 +126,14 @@ interface ProfileRaw {
const router = Router();
router.get('/calendar/today', handleErrorAsync(async (req, res) => {
- return res.json(eventsSummary(
+ return res.json(await eventsSummary(
Day.today(),
req.locales[config.locale],
));
}));
router.get('/calendar/:year-:month-:day', handleErrorAsync(async (req, res) => {
- return res.json(eventsSummary(
+ return res.json(await eventsSummary(
new Day(parseInt(req.params.year), parseInt(req.params.month), parseInt(req.params.day)),
req.locales[config.locale],
));
@@ -142,6 +142,7 @@ router.get('/calendar/:year-:month-:day', handleErrorAsync(async (req, res) => {
const routeBase = `/queer-calendar-${config.locale}`;
router.get(`${routeBase}.ics`, handleErrorAsync(async (req, res) => {
+ const calendar = await loadCalendar();
const events: Record = {};
for (const year of calendar.getAllYears()) {
events[year.year] = year.events;
@@ -151,6 +152,7 @@ router.get(`${routeBase}.ics`, handleErrorAsync(async (req, res) => {
}));
router.get(generateURIEncodedPathAlternative(`${routeBase}-@:username.ics`), handleErrorAsync(async (req, res) => {
+ const calendar = await loadCalendar();
const profiles = await req.db.all>(SQL`
SELECT events, customEvents FROM profiles p
LEFT JOIN users u ON p.userId = u.id
@@ -176,6 +178,7 @@ router.get(generateURIEncodedPathAlternative(`${routeBase}-@:username.ics`), han
}));
router.get(`${routeBase}-:year-:uuid.ics`, handleErrorAsync(async (req, res) => {
+ const calendar = await loadCalendar();
const year = calendar.getYear(parseInt(req.params.year));
if (!year) {
return res.status(404).json({ error: 'Not found' });
@@ -185,6 +188,7 @@ router.get(`${routeBase}-:year-:uuid.ics`, handleErrorAsync(async (req, res) =>
}));
router.get(`${routeBase}-:year.ics`, handleErrorAsync(async (req, res) => {
+ const calendar = await loadCalendar();
const year = calendar.getYear(parseInt(req.params.year));
if (!year) {
return res.status(404).json({ error: 'Not found' });
diff --git a/src/calendar/calendar.ts b/src/calendar/calendar.ts
index e1f0a6f3b..e86c2ddb6 100644
--- a/src/calendar/calendar.ts
+++ b/src/calendar/calendar.ts
@@ -1,18 +1,8 @@
-import localEvents from '../../data/calendar/events.ts';
-import rawNamedays from '../../data/names/namedays.json' with { type: 'json' };
-import internationalEvents from '../../locale/_/calendar/events.ts';
+import internationalEvents from '~/locale/_/calendar/events.ts';
+import { Calendar } from '~/src/calendar/helpers.ts';
+import type { Event } from '~/src/calendar/helpers.ts';
-import { Calendar, Event, EventLevel, day } from './helpers.ts';
-
-const _namedays = Object.entries(rawNamedays as Record)
- .flatMap(([name, namedays]) => {
- return namedays.map((nameday) => {
- const [m, d] = nameday.split('-');
- return new Event(`nameday$${name}`, null, parseInt(m), day(parseInt(d)), EventLevel.Nameday);
- });
- });
-
-export const buildCalendar = (baseUrl: string) => {
+export const buildCalendar = (localEvents: Event[], baseUrl: string) => {
return new Calendar(
[...internationalEvents, ...localEvents], // TODO , ...namedays
baseUrl,
diff --git a/src/data.ts b/src/data.ts
index 54fd046e0..500502710 100644
--- a/src/data.ts
+++ b/src/data.ts
@@ -4,6 +4,7 @@ import { buildDict, buildList } from './helpers.ts';
import type { Config } from '~/locale/config.ts';
import type { PronounExamplesData } from '~/locale/data.ts';
+import { buildCalendar } from '~/src/calendar/calendar.ts';
export const getLocale = () => {
return useRuntimeConfig().public.locale;
@@ -97,3 +98,17 @@ export const loadNameCount = async () => {
return {};
}
};
+
+export const loadCalendar = async () => {
+ const runtimeConfig = useRuntimeConfig();
+ const localEvents = (await import(`~/locale/${getLocale()}/calendar/events.ts`)).default;
+ /* const rawNamedays = (await import(`~/locale/${config.locale}/names/namedays.json`)).default;
+ const namedays = Object.entries(rawNamedays as Record)
+ .flatMap(([name, namedays]) => {
+ return namedays.map((nameday) => {
+ const [m, d] = nameday.split('-');
+ return new Event(`nameday$${name}`, null, parseInt(m), day(parseInt(d)), EventLevel.Nameday);
+ });
+ }); */
+ return buildCalendar(localEvents, runtimeConfig.public.baseUrl);
+};
diff --git a/types/virtual.d.ts b/types/virtual.d.ts
new file mode 100644
index 000000000..6f2d3b391
--- /dev/null
+++ b/types/virtual.d.ts
@@ -0,0 +1,6 @@
+declare module '#virtual/calendar/events.ts' {
+ import type { Event } from '~/src/calendar/helpers.ts';
+
+ declare const localEventsByLocale: Record Promise>;
+ export default localEventsByLocale;
+}