feat(vacay): add manual school holiday calendars

This commit is contained in:
Maurice
2026-09-12 11:37:57 +02:00
committed by Maurice
parent 4f8605b529
commit 41b18f970c
55 changed files with 1562 additions and 34 deletions
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': 'تم حفظ التغيير، لكن تعذر تحديث القائمة. حاول تحميلها مجددًا.',
'schoolCatalog.title': 'العطلات المدرسية',
'schoolCatalog.hint': 'أدر المناطق المشتركة. يمكن للجميع اختيارها في إعدادات الإجازات.',
'schoolCatalog.periodHint': 'يشمل كلا التاريخين. أضف الفترات لكل عام دراسي.',
'schoolCatalog.region': 'المنطقة أو المنطقة التعليمية',
'schoolCatalog.name': 'اسم العطلة',
'schoolCatalog.start': 'تاريخ البدء',
'schoolCatalog.end': 'تاريخ الانتهاء',
'schoolCatalog.addPeriod': 'إضافة فترة عطلة',
'schoolCatalog.invalid': 'تحقق من الأسماء والتواريخ.',
'schoolCatalog.discard': 'تجاهل التغييرات غير المحفوظة؟',
'schoolCatalog.country': 'البلد',
'schoolCatalog.deleteHint': 'احذف المناطق أولاً. لا يمكن حذف المناطق المستخدمة.',
'schoolCatalog.deleteCountry': 'حذف البلد',
'schoolCatalog.addCountry': 'إضافة بلد',
'schoolCatalog.code': 'رمز البلد (مثل US)',
'schoolCatalog.empty': 'لا توجد مناطق بعد.',
'schoolCatalog.addRegion': 'إضافة منطقة',
'schoolCatalog.offline': 'اتصل بالإنترنت لإدارة العطلات المدرسية.',
'schoolCatalog.loadError': 'تعذر تحميل المناطق. أعد فتح الإعدادات للمحاولة مجددًا.',
'schoolCatalog.retry': 'إعادة المحاولة',
'vacay.subtitle': 'خطط وأدر أيام الإجازة',
'vacay.settings': 'الإعدادات',
'vacay.year': 'السنة',
+22
View File
@@ -1,6 +1,28 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError':
'A alteração foi salva, mas a lista não pôde ser atualizada. Tente carregá-la novamente.',
'schoolCatalog.title': 'Férias escolares',
'schoolCatalog.hint': 'Gerencie regiões compartilhadas. Todos podem selecioná-las nas configurações de férias.',
'schoolCatalog.periodHint': 'As duas datas estão incluídas. Adicione períodos para cada ano letivo.',
'schoolCatalog.region': 'Região ou distrito escolar',
'schoolCatalog.name': 'Nome das férias',
'schoolCatalog.start': 'Data inicial',
'schoolCatalog.end': 'Data final',
'schoolCatalog.addPeriod': 'Adicionar período',
'schoolCatalog.invalid': 'Confira os nomes e as datas.',
'schoolCatalog.discard': 'Descartar alterações não salvas?',
'schoolCatalog.country': 'País',
'schoolCatalog.deleteHint': 'Remova primeiro as regiões. Regiões em uso não podem ser excluídas.',
'schoolCatalog.deleteCountry': 'Excluir país',
'schoolCatalog.addCountry': 'Adicionar país',
'schoolCatalog.code': 'Código do país (ex.: US)',
'schoolCatalog.empty': 'Nenhuma região ainda.',
'schoolCatalog.addRegion': 'Adicionar região',
'schoolCatalog.offline': 'Conecte-se à internet para gerenciar as férias escolares.',
'schoolCatalog.loadError': 'Não foi possível carregar as regiões. Abra as configurações novamente.',
'schoolCatalog.retry': 'Tentar novamente',
'vacay.subtitle': 'Planeje e gerencie dias de férias',
'vacay.settings': 'Configurações',
'vacay.year': 'Ano',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': 'El canvi s’ha desat, però no s’ha pogut actualitzar la llista. Torna a carregar-la.',
'schoolCatalog.title': 'Vacances escolars',
'schoolCatalog.hint': 'Gestiona regions compartides. Tothom les pot triar als ajustos de vacances.',
'schoolCatalog.periodHint': 'Ambdues dates estan incloses. Afegeix períodes per a cada curs escolar.',
'schoolCatalog.region': 'Regió o districte escolar',
'schoolCatalog.name': 'Nom de les vacances',
'schoolCatalog.start': 'Data inicial',
'schoolCatalog.end': 'Data final',
'schoolCatalog.addPeriod': 'Afegeix un període',
'schoolCatalog.invalid': 'Revisa els noms i les dates.',
'schoolCatalog.discard': 'Vols descartar els canvis sense desar?',
'schoolCatalog.country': 'País',
'schoolCatalog.deleteHint': 'Elimina primer les regions. Les regions en ús no es poden eliminar.',
'schoolCatalog.deleteCountry': 'Elimina el país',
'schoolCatalog.addCountry': 'Afegeix un país',
'schoolCatalog.code': 'Codi del país (p. ex. US)',
'schoolCatalog.empty': 'Encara no hi ha regions.',
'schoolCatalog.addRegion': 'Afegeix una regió',
'schoolCatalog.offline': 'Connecta’t a Internet per gestionar les vacances escolars.',
'schoolCatalog.loadError': 'No s’han pogut carregar les regions. Torna a obrir els ajustos.',
'schoolCatalog.retry': 'Torna-ho a provar',
'vacay.subtitle': 'Planifica i gestiona dies de vacances',
'vacay.settings': 'Configuració',
'vacay.year': 'Any',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': 'Změna byla uložena, ale seznam se nepodařilo obnovit. Zkuste jej načíst znovu.',
'schoolCatalog.title': 'Školní prázdniny',
'schoolCatalog.hint': 'Spravujte sdílené regiony. Všichni je mohou vybrat v nastavení dovolené.',
'schoolCatalog.periodHint': 'Oba krajní dny jsou zahrnuty. Přidejte období pro každý školní rok.',
'schoolCatalog.region': 'Region nebo školní obvod',
'schoolCatalog.name': 'Název prázdnin',
'schoolCatalog.start': 'Datum začátku',
'schoolCatalog.end': 'Datum konce',
'schoolCatalog.addPeriod': 'Přidat období',
'schoolCatalog.invalid': 'Zkontrolujte názvy a data.',
'schoolCatalog.discard': 'Zahodit neuložené změny?',
'schoolCatalog.country': 'Země',
'schoolCatalog.deleteHint': 'Nejprve odstraňte regiony. Používané regiony nelze smazat.',
'schoolCatalog.deleteCountry': 'Smazat zemi',
'schoolCatalog.addCountry': 'Přidat zemi',
'schoolCatalog.code': 'Kód země (např. US)',
'schoolCatalog.empty': 'Zatím žádné regiony.',
'schoolCatalog.addRegion': 'Přidat region',
'schoolCatalog.offline': 'Pro správu školních prázdnin se připojte k internetu.',
'schoolCatalog.loadError': 'Regiony se nepodařilo načíst. Znovu otevřete nastavení.',
'schoolCatalog.retry': 'Zkusit znovu',
'vacay.subtitle': 'Plánování a správa dovolené',
'vacay.settings': 'Nastavení',
'vacay.year': 'Rok',
+22
View File
@@ -1,6 +1,28 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError':
'Die Änderung wurde gespeichert, aber die Liste konnte nicht neu geladen werden. Versuche es erneut.',
'schoolCatalog.title': 'Schulferien',
'schoolCatalog.hint': 'Verwalte gemeinsame Ferienregionen. Alle können sie in den Urlaubseinstellungen auswählen.',
'schoolCatalog.periodHint': 'Beide Grenztage zählen mit. Ergänze Zeiträume für jedes Schuljahr.',
'schoolCatalog.region': 'Region oder Schulbezirk',
'schoolCatalog.name': 'Name der Ferien',
'schoolCatalog.start': 'Startdatum',
'schoolCatalog.end': 'Enddatum',
'schoolCatalog.addPeriod': 'Ferienzeitraum hinzufügen',
'schoolCatalog.invalid': 'Prüfe Namen und Datumsangaben.',
'schoolCatalog.discard': 'Ungespeicherte Änderungen verwerfen?',
'schoolCatalog.country': 'Land',
'schoolCatalog.deleteHint': 'Entferne zuerst die Regionen. Verwendete Regionen können nicht gelöscht werden.',
'schoolCatalog.deleteCountry': 'Land löschen',
'schoolCatalog.addCountry': 'Land hinzufügen',
'schoolCatalog.code': 'Ländercode (z. B. US)',
'schoolCatalog.empty': 'Noch keine Regionen vorhanden.',
'schoolCatalog.addRegion': 'Region hinzufügen',
'schoolCatalog.offline': 'Verbinde dich mit dem Internet, um Schulferien zu verwalten.',
'schoolCatalog.loadError': 'Ferienregionen konnten nicht geladen werden. Öffne die Einstellungen erneut.',
'schoolCatalog.retry': 'Erneut versuchen',
'vacay.subtitle': 'Urlaubstage planen und verwalten',
'vacay.settings': 'Einstellungen',
'vacay.year': 'Jahr',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': 'The change was saved, but the list could not be refreshed. Retry loading it.',
'schoolCatalog.title': 'School holidays',
'schoolCatalog.hint': 'Manage shared school holiday regions. Everyone can select them in vacation settings.',
'schoolCatalog.periodHint': 'Dates include both days. Add periods for each school year.',
'schoolCatalog.region': 'Region or school district',
'schoolCatalog.name': 'Holiday name',
'schoolCatalog.start': 'Start date',
'schoolCatalog.end': 'End date',
'schoolCatalog.addPeriod': 'Add holiday period',
'schoolCatalog.invalid': 'Check the names and dates.',
'schoolCatalog.discard': 'Discard unsaved changes?',
'schoolCatalog.country': 'Country',
'schoolCatalog.deleteHint': 'Remove regions first. Regions used by calendars cannot be deleted.',
'schoolCatalog.deleteCountry': 'Delete country',
'schoolCatalog.addCountry': 'Add country',
'schoolCatalog.code': 'Country code (e.g. US)',
'schoolCatalog.empty': 'No regions yet.',
'schoolCatalog.addRegion': 'Add region',
'schoolCatalog.offline': 'Connect to the internet to manage school holidays.',
'schoolCatalog.loadError': 'Could not load school holiday regions. Reopen settings to retry.',
'schoolCatalog.retry': 'Retry',
'vacay.subtitle': 'Plan and manage vacation days',
'vacay.settings': 'Settings',
'vacay.year': 'Year',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': 'El cambio se guardó, pero no se pudo actualizar la lista. Intenta cargarla de nuevo.',
'schoolCatalog.title': 'Vacaciones escolares',
'schoolCatalog.hint': 'Gestiona regiones compartidas. Todos pueden elegirlas en los ajustes de vacaciones.',
'schoolCatalog.periodHint': 'Ambas fechas están incluidas. Añade períodos para cada curso escolar.',
'schoolCatalog.region': 'Región o distrito escolar',
'schoolCatalog.name': 'Nombre de las vacaciones',
'schoolCatalog.start': 'Fecha de inicio',
'schoolCatalog.end': 'Fecha de fin',
'schoolCatalog.addPeriod': 'Añadir período',
'schoolCatalog.invalid': 'Revisa los nombres y las fechas.',
'schoolCatalog.discard': '¿Descartar los cambios sin guardar?',
'schoolCatalog.country': 'País',
'schoolCatalog.deleteHint': 'Elimina primero las regiones. Las regiones en uso no se pueden eliminar.',
'schoolCatalog.deleteCountry': 'Eliminar país',
'schoolCatalog.addCountry': 'Añadir país',
'schoolCatalog.code': 'Código de país (p. ej. US)',
'schoolCatalog.empty': 'Todavía no hay regiones.',
'schoolCatalog.addRegion': 'Añadir región',
'schoolCatalog.offline': 'Conéctate a Internet para gestionar las vacaciones escolares.',
'schoolCatalog.loadError': 'No se pudieron cargar las regiones. Vuelve a abrir los ajustes.',
'schoolCatalog.retry': 'Reintentar',
'vacay.subtitle': 'Planifica y gestiona días de vacaciones',
'vacay.settings': 'Ajustes',
'vacay.year': 'Año',
+22
View File
@@ -1,6 +1,28 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError':
'La modification a été enregistrée, mais la liste n’a pas pu être actualisée. Réessayez de la charger.',
'schoolCatalog.title': 'Vacances scolaires',
'schoolCatalog.hint': 'Gérez les régions partagées. Chacun peut les choisir dans les paramètres des congés.',
'schoolCatalog.periodHint': 'Les deux dates sont incluses. Ajoutez les périodes de chaque année scolaire.',
'schoolCatalog.region': 'Région ou district scolaire',
'schoolCatalog.name': 'Nom des vacances',
'schoolCatalog.start': 'Date de début',
'schoolCatalog.end': 'Date de fin',
'schoolCatalog.addPeriod': 'Ajouter une période',
'schoolCatalog.invalid': 'Vérifiez les noms et les dates.',
'schoolCatalog.discard': 'Abandonner les modifications non enregistrées ?',
'schoolCatalog.country': 'Pays',
'schoolCatalog.deleteHint': 'Supprimez les régions en premier. Les régions utilisées ne peuvent pas être supprimées.',
'schoolCatalog.deleteCountry': 'Supprimer le pays',
'schoolCatalog.addCountry': 'Ajouter un pays',
'schoolCatalog.code': 'Code du pays (ex. US)',
'schoolCatalog.empty': 'Aucune région pour le moment.',
'schoolCatalog.addRegion': 'Ajouter une région',
'schoolCatalog.offline': 'Connectez-vous à Internet pour gérer les vacances scolaires.',
'schoolCatalog.loadError': 'Impossible de charger les régions. Rouvrez les paramètres pour réessayer.',
'schoolCatalog.retry': 'Réessayer',
'vacay.subtitle': 'Planifiez et gérez vos jours de congés',
'vacay.settings': 'Paramètres',
'vacay.year': 'Année',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': 'Η αλλαγή αποθηκεύτηκε, αλλά η λίστα δεν ανανεώθηκε. Δοκιμάστε να τη φορτώσετε ξανά.',
'schoolCatalog.title': 'Σχολικές διακοπές',
'schoolCatalog.hint': 'Διαχειριστείτε κοινές περιοχές. Όλοι μπορούν να τις επιλέξουν στις ρυθμίσεις αδειών.',
'schoolCatalog.periodHint': 'Περιλαμβάνονται και οι δύο ημερομηνίες. Προσθέστε περιόδους για κάθε σχολικό έτος.',
'schoolCatalog.region': 'Περιοχή ή σχολική περιφέρεια',
'schoolCatalog.name': 'Όνομα διακοπών',
'schoolCatalog.start': 'Ημερομηνία έναρξης',
'schoolCatalog.end': 'Ημερομηνία λήξης',
'schoolCatalog.addPeriod': 'Προσθήκη περιόδου',
'schoolCatalog.invalid': 'Ελέγξτε τα ονόματα και τις ημερομηνίες.',
'schoolCatalog.discard': 'Απόρριψη μη αποθηκευμένων αλλαγών;',
'schoolCatalog.country': 'Χώρα',
'schoolCatalog.deleteHint': 'Αφαιρέστε πρώτα τις περιοχές. Οι περιοχές που χρησιμοποιούνται δεν διαγράφονται.',
'schoolCatalog.deleteCountry': 'Διαγραφή χώρας',
'schoolCatalog.addCountry': 'Προσθήκη χώρας',
'schoolCatalog.code': 'Κωδικός χώρας (π.χ. US)',
'schoolCatalog.empty': 'Δεν υπάρχουν ακόμη περιοχές.',
'schoolCatalog.addRegion': 'Προσθήκη περιοχής',
'schoolCatalog.offline': 'Συνδεθείτε στο διαδίκτυο για να διαχειριστείτε τις σχολικές διακοπές.',
'schoolCatalog.loadError': 'Δεν ήταν δυνατή η φόρτωση των περιοχών. Ανοίξτε ξανά τις ρυθμίσεις.',
'schoolCatalog.retry': 'Δοκιμή ξανά',
'vacay.subtitle': 'Σχεδιάστε και διαχειριστείτε ημέρες διακοπών',
'vacay.settings': 'Ρυθμίσεις',
'vacay.year': 'Έτος',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': 'A módosítás mentve, de a lista nem frissült. Próbáld újra betölteni.',
'schoolCatalog.title': 'Iskolai szünetek',
'schoolCatalog.hint': 'Kezeld a közös régiókat. Mindenki kiválaszthatja őket a szabadság beállításaiban.',
'schoolCatalog.periodHint': 'Mindkét határnap beleszámít. Adj hozzá időszakokat minden tanévhez.',
'schoolCatalog.region': 'Régió vagy tankerület',
'schoolCatalog.name': 'Szünet neve',
'schoolCatalog.start': 'Kezdő dátum',
'schoolCatalog.end': 'Záró dátum',
'schoolCatalog.addPeriod': 'Időszak hozzáadása',
'schoolCatalog.invalid': 'Ellenőrizd a neveket és a dátumokat.',
'schoolCatalog.discard': 'Elveted a nem mentett módosításokat?',
'schoolCatalog.country': 'Ország',
'schoolCatalog.deleteHint': 'Először távolítsd el a régiókat. A használt régiók nem törölhetők.',
'schoolCatalog.deleteCountry': 'Ország törlése',
'schoolCatalog.addCountry': 'Ország hozzáadása',
'schoolCatalog.code': 'Országkód (pl. US)',
'schoolCatalog.empty': 'Még nincsenek régiók.',
'schoolCatalog.addRegion': 'Régió hozzáadása',
'schoolCatalog.offline': 'Az iskolai szünetek kezeléséhez csatlakozz az internethez.',
'schoolCatalog.loadError': 'A régiók nem tölthetők be. Nyisd meg újra a beállításokat.',
'schoolCatalog.retry': 'Újrapróbálkozás',
'vacay.subtitle': 'Szabadságnapok tervezése és kezelése',
'vacay.settings': 'Beállítások',
'vacay.year': 'Év',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': 'Perubahan disimpan, tetapi daftar gagal diperbarui. Coba muat ulang.',
'schoolCatalog.title': 'Liburan sekolah',
'schoolCatalog.hint': 'Kelola wilayah bersama. Semua orang dapat memilihnya di pengaturan cuti.',
'schoolCatalog.periodHint': 'Kedua tanggal termasuk. Tambahkan periode untuk setiap tahun ajaran.',
'schoolCatalog.region': 'Wilayah atau distrik sekolah',
'schoolCatalog.name': 'Nama liburan',
'schoolCatalog.start': 'Tanggal mulai',
'schoolCatalog.end': 'Tanggal selesai',
'schoolCatalog.addPeriod': 'Tambah periode liburan',
'schoolCatalog.invalid': 'Periksa nama dan tanggal.',
'schoolCatalog.discard': 'Buang perubahan yang belum disimpan?',
'schoolCatalog.country': 'Negara',
'schoolCatalog.deleteHint': 'Hapus wilayah terlebih dahulu. Wilayah yang digunakan tidak dapat dihapus.',
'schoolCatalog.deleteCountry': 'Hapus negara',
'schoolCatalog.addCountry': 'Tambah negara',
'schoolCatalog.code': 'Kode negara (mis. US)',
'schoolCatalog.empty': 'Belum ada wilayah.',
'schoolCatalog.addRegion': 'Tambah wilayah',
'schoolCatalog.offline': 'Hubungkan ke internet untuk mengelola liburan sekolah.',
'schoolCatalog.loadError': 'Wilayah gagal dimuat. Buka kembali pengaturan.',
'schoolCatalog.retry': 'Coba lagi',
'vacay.subtitle': 'Rencanakan dan kelola hari cuti',
'vacay.settings': 'Pengaturan',
'vacay.year': 'Tahun',
+22
View File
@@ -1,6 +1,28 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError':
'La modifica è stata salvata, ma non è stato possibile aggiornare l’elenco. Riprova a caricarlo.',
'schoolCatalog.title': 'Vacanze scolastiche',
'schoolCatalog.hint': 'Gestisci le regioni condivise. Tutti possono sceglierle nelle impostazioni delle ferie.',
'schoolCatalog.periodHint': 'Entrambe le date sono incluse. Aggiungi i periodi di ogni anno scolastico.',
'schoolCatalog.region': 'Regione o distretto scolastico',
'schoolCatalog.name': 'Nome delle vacanze',
'schoolCatalog.start': 'Data di inizio',
'schoolCatalog.end': 'Data di fine',
'schoolCatalog.addPeriod': 'Aggiungi periodo',
'schoolCatalog.invalid': 'Controlla i nomi e le date.',
'schoolCatalog.discard': 'Scartare le modifiche non salvate?',
'schoolCatalog.country': 'Paese',
'schoolCatalog.deleteHint': 'Rimuovi prima le regioni. Le regioni in uso non possono essere eliminate.',
'schoolCatalog.deleteCountry': 'Elimina paese',
'schoolCatalog.addCountry': 'Aggiungi paese',
'schoolCatalog.code': 'Codice paese (es. US)',
'schoolCatalog.empty': 'Nessuna regione presente.',
'schoolCatalog.addRegion': 'Aggiungi regione',
'schoolCatalog.offline': 'Connettiti a Internet per gestire le vacanze scolastiche.',
'schoolCatalog.loadError': 'Impossibile caricare le regioni. Riapri le impostazioni.',
'schoolCatalog.retry': 'Riprova',
'vacay.subtitle': 'Pianifica e gestisci i giorni di ferie',
'vacay.settings': 'Impostazioni',
'vacay.year': 'Anno',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': '変更は保存されましたが、一覧を更新できませんでした。再読み込みしてください。',
'schoolCatalog.title': '学校の休暇',
'schoolCatalog.hint': '共有する地域を管理します。全員が休暇設定で選択できます。',
'schoolCatalog.periodHint': '開始日と終了日を含みます。各学年の期間を追加してください。',
'schoolCatalog.region': '地域または学区',
'schoolCatalog.name': '休暇名',
'schoolCatalog.start': '開始日',
'schoolCatalog.end': '終了日',
'schoolCatalog.addPeriod': '休暇期間を追加',
'schoolCatalog.invalid': '名前と日付を確認してください。',
'schoolCatalog.discard': '未保存の変更を破棄しますか?',
'schoolCatalog.country': '国',
'schoolCatalog.deleteHint': '先に地域を削除してください。使用中の地域は削除できません。',
'schoolCatalog.deleteCountry': '国を削除',
'schoolCatalog.addCountry': '国を追加',
'schoolCatalog.code': '国コード(例:US)',
'schoolCatalog.empty': '地域はまだありません。',
'schoolCatalog.addRegion': '地域を追加',
'schoolCatalog.offline': '学校の休暇を管理するにはインターネットに接続してください。',
'schoolCatalog.loadError': '地域を読み込めませんでした。設定を開き直してください。',
'schoolCatalog.retry': '再試行',
'vacay.subtitle': '休暇日数の計画と管理',
'vacay.settings': '設定',
'vacay.year': '年',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': '변경 사항은 저장했지만 목록을 새로 고치지 못했습니다. 다시 불러오세요.',
'schoolCatalog.title': '학교 방학',
'schoolCatalog.hint': '공유 지역을 관리하세요. 누구나 휴가 설정에서 선택할 수 있습니다.',
'schoolCatalog.periodHint': '시작일과 종료일을 모두 포함합니다. 학년도별 기간을 추가하세요.',
'schoolCatalog.region': '지역 또는 학군',
'schoolCatalog.name': '방학 이름',
'schoolCatalog.start': '시작일',
'schoolCatalog.end': '종료일',
'schoolCatalog.addPeriod': '방학 기간 추가',
'schoolCatalog.invalid': '이름과 날짜를 확인하세요.',
'schoolCatalog.discard': '저장하지 않은 변경 사항을 취소할까요?',
'schoolCatalog.country': '국가',
'schoolCatalog.deleteHint': '먼저 지역을 삭제하세요. 사용 중인 지역은 삭제할 수 없습니다.',
'schoolCatalog.deleteCountry': '국가 삭제',
'schoolCatalog.addCountry': '국가 추가',
'schoolCatalog.code': '국가 코드(예: US)',
'schoolCatalog.empty': '아직 지역이 없습니다.',
'schoolCatalog.addRegion': '지역 추가',
'schoolCatalog.offline': '학교 방학을 관리하려면 인터넷에 연결하세요.',
'schoolCatalog.loadError': '지역을 불러오지 못했습니다. 설정을 다시 여세요.',
'schoolCatalog.retry': '다시 시도',
'vacay.subtitle': '휴가 일수를 계획하고 관리하세요',
'vacay.settings': '설정',
'vacay.year': '연도',
+22
View File
@@ -1,6 +1,28 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError':
'De wijziging is opgeslagen, maar de lijst kon niet worden vernieuwd. Probeer deze opnieuw te laden.',
'schoolCatalog.title': 'Schoolvakanties',
'schoolCatalog.hint': 'Beheer gedeelde vakantieregio’s. Iedereen kan ze kiezen in de vakantie-instellingen.',
'schoolCatalog.periodHint': 'Beide datums tellen mee. Voeg perioden voor elk schooljaar toe.',
'schoolCatalog.region': 'Regio of schooldistrict',
'schoolCatalog.name': 'Naam van de vakantie',
'schoolCatalog.start': 'Begindatum',
'schoolCatalog.end': 'Einddatum',
'schoolCatalog.addPeriod': 'Vakantieperiode toevoegen',
'schoolCatalog.invalid': 'Controleer de namen en datums.',
'schoolCatalog.discard': 'Niet-opgeslagen wijzigingen verwijderen?',
'schoolCatalog.country': 'Land',
'schoolCatalog.deleteHint': 'Verwijder eerst de regio’s. Regio’s die worden gebruikt kunnen niet worden verwijderd.',
'schoolCatalog.deleteCountry': 'Land verwijderen',
'schoolCatalog.addCountry': 'Land toevoegen',
'schoolCatalog.code': 'Landcode (bijv. US)',
'schoolCatalog.empty': 'Nog geen regio’s.',
'schoolCatalog.addRegion': 'Regio toevoegen',
'schoolCatalog.offline': 'Maak verbinding met internet om schoolvakanties te beheren.',
'schoolCatalog.loadError': 'Vakantieregio’s konden niet worden geladen. Open de instellingen opnieuw.',
'schoolCatalog.retry': 'Opnieuw proberen',
'vacay.subtitle': 'Plan en beheer vakantiedagen',
'vacay.settings': 'Instellingen',
'vacay.year': 'Jaar',
+22
View File
@@ -1,6 +1,28 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError':
'Zmiana została zapisana, ale nie udało się odświeżyć listy. Spróbuj wczytać ją ponownie.',
'schoolCatalog.title': 'Ferie szkolne',
'schoolCatalog.hint': 'Zarządzaj wspólnymi regionami. Każdy może je wybrać w ustawieniach urlopu.',
'schoolCatalog.periodHint': 'Obie daty są wliczone. Dodaj okresy na każdy rok szkolny.',
'schoolCatalog.region': 'Region lub okręg szkolny',
'schoolCatalog.name': 'Nazwa ferii',
'schoolCatalog.start': 'Data rozpoczęcia',
'schoolCatalog.end': 'Data zakończenia',
'schoolCatalog.addPeriod': 'Dodaj okres',
'schoolCatalog.invalid': 'Sprawdź nazwy i daty.',
'schoolCatalog.discard': 'Odrzucić niezapisane zmiany?',
'schoolCatalog.country': 'Kraj',
'schoolCatalog.deleteHint': 'Najpierw usuń regiony. Nie można usunąć używanych regionów.',
'schoolCatalog.deleteCountry': 'Usuń kraj',
'schoolCatalog.addCountry': 'Dodaj kraj',
'schoolCatalog.code': 'Kod kraju (np. US)',
'schoolCatalog.empty': 'Brak regionów.',
'schoolCatalog.addRegion': 'Dodaj region',
'schoolCatalog.offline': 'Połącz się z internetem, aby zarządzać feriami szkolnymi.',
'schoolCatalog.loadError': 'Nie udało się wczytać regionów. Otwórz ponownie ustawienia.',
'schoolCatalog.retry': 'Spróbuj ponownie',
'vacay.subtitle': 'Planuj i zarządzaj dniami urlopu',
'vacay.settings': 'Ustawienia',
'vacay.year': 'Rok',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': 'Изменение сохранено, но обновить список не удалось. Попробуйте загрузить его снова.',
'schoolCatalog.title': 'Школьные каникулы',
'schoolCatalog.hint': 'Управляйте общими регионами. Каждый может выбрать их в настройках отпуска.',
'schoolCatalog.periodHint': 'Обе даты включены. Добавляйте периоды на каждый учебный год.',
'schoolCatalog.region': 'Регион или школьный округ',
'schoolCatalog.name': 'Название каникул',
'schoolCatalog.start': 'Дата начала',
'schoolCatalog.end': 'Дата окончания',
'schoolCatalog.addPeriod': 'Добавить период',
'schoolCatalog.invalid': 'Проверьте названия и даты.',
'schoolCatalog.discard': 'Отменить несохранённые изменения?',
'schoolCatalog.country': 'Страна',
'schoolCatalog.deleteHint': 'Сначала удалите регионы. Используемые регионы удалить нельзя.',
'schoolCatalog.deleteCountry': 'Удалить страну',
'schoolCatalog.addCountry': 'Добавить страну',
'schoolCatalog.code': 'Код страны (например, US)',
'schoolCatalog.empty': 'Регионов пока нет.',
'schoolCatalog.addRegion': 'Добавить регион',
'schoolCatalog.offline': 'Подключитесь к интернету для управления школьными каникулами.',
'schoolCatalog.loadError': 'Не удалось загрузить регионы. Откройте настройки заново.',
'schoolCatalog.retry': 'Повторить',
'vacay.subtitle': 'Планируйте и управляйте днями отпуска',
'vacay.settings': 'Настройки',
'vacay.year': 'Год',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': 'Ändringen sparades, men listan kunde inte uppdateras. Försök läsa in den igen.',
'schoolCatalog.title': 'Skollov',
'schoolCatalog.hint': 'Hantera gemensamma regioner. Alla kan välja dem i semesterinställningarna.',
'schoolCatalog.periodHint': 'Båda datumen ingår. Lägg till perioder för varje läsår.',
'schoolCatalog.region': 'Region eller skoldistrikt',
'schoolCatalog.name': 'Lovets namn',
'schoolCatalog.start': 'Startdatum',
'schoolCatalog.end': 'Slutdatum',
'schoolCatalog.addPeriod': 'Lägg till lovperiod',
'schoolCatalog.invalid': 'Kontrollera namn och datum.',
'schoolCatalog.discard': 'Kasta osparade ändringar?',
'schoolCatalog.country': 'Land',
'schoolCatalog.deleteHint': 'Ta bort regionerna först. Regioner som används kan inte tas bort.',
'schoolCatalog.deleteCountry': 'Ta bort land',
'schoolCatalog.addCountry': 'Lägg till land',
'schoolCatalog.code': 'Landskod (t.ex. US)',
'schoolCatalog.empty': 'Inga regioner ännu.',
'schoolCatalog.addRegion': 'Lägg till region',
'schoolCatalog.offline': 'Anslut till internet för att hantera skollov.',
'schoolCatalog.loadError': 'Kunde inte läsa in regionerna. Öppna inställningarna igen.',
'schoolCatalog.retry': 'Försök igen',
'vacay.subtitle': 'Planera och hantera semesterdagar',
'vacay.settings': 'Inställningar',
'vacay.year': 'År',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': 'Değişiklik kaydedildi, ancak liste yenilenemedi. Yeniden yüklemeyi deneyin.',
'schoolCatalog.title': 'Okul tatilleri',
'schoolCatalog.hint': 'Ortak bölgeleri yönetin. Herkes izin ayarlarından bunları seçebilir.',
'schoolCatalog.periodHint': 'Her iki tarih de dahildir. Her öğretim yılı için dönemler ekleyin.',
'schoolCatalog.region': 'Bölge veya okul bölgesi',
'schoolCatalog.name': 'Tatil adı',
'schoolCatalog.start': 'Başlangıç tarihi',
'schoolCatalog.end': 'Bitiş tarihi',
'schoolCatalog.addPeriod': 'Tatil dönemi ekle',
'schoolCatalog.invalid': 'Adları ve tarihleri kontrol edin.',
'schoolCatalog.discard': 'Kaydedilmemiş değişiklikler silinsin mi?',
'schoolCatalog.country': 'Ülke',
'schoolCatalog.deleteHint': 'Önce bölgeleri kaldırın. Kullanılan bölgeler silinemez.',
'schoolCatalog.deleteCountry': 'Ülkeyi sil',
'schoolCatalog.addCountry': 'Ülke ekle',
'schoolCatalog.code': 'Ülke kodu (ör. US)',
'schoolCatalog.empty': 'Henüz bölge yok.',
'schoolCatalog.addRegion': 'Bölge ekle',
'schoolCatalog.offline': 'Okul tatillerini yönetmek için internete bağlanın.',
'schoolCatalog.loadError': 'Bölgeler yüklenemedi. Ayarları yeniden açın.',
'schoolCatalog.retry': 'Tekrar dene',
'vacay.subtitle': 'Tatil günlerini planlayın ve yönetin',
'vacay.settings': 'Ayarlar',
'vacay.year': 'Yıl',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': 'Зміну збережено, але список не вдалося оновити. Спробуйте завантажити його знову.',
'schoolCatalog.title': 'Шкільні канікули',
'schoolCatalog.hint': 'Керуйте спільними регіонами. Кожен може вибрати їх у налаштуваннях відпустки.',
'schoolCatalog.periodHint': 'Обидві дати включено. Додавайте періоди на кожен навчальний рік.',
'schoolCatalog.region': 'Регіон або шкільний округ',
'schoolCatalog.name': 'Назва канікул',
'schoolCatalog.start': 'Дата початку',
'schoolCatalog.end': 'Дата завершення',
'schoolCatalog.addPeriod': 'Додати період',
'schoolCatalog.invalid': 'Перевірте назви й дати.',
'schoolCatalog.discard': 'Відкинути незбережені зміни?',
'schoolCatalog.country': 'Країна',
'schoolCatalog.deleteHint': 'Спочатку видаліть регіони. Регіони, що використовуються, видалити не можна.',
'schoolCatalog.deleteCountry': 'Видалити країну',
'schoolCatalog.addCountry': 'Додати країну',
'schoolCatalog.code': 'Код країни (наприклад, US)',
'schoolCatalog.empty': 'Регіонів поки немає.',
'schoolCatalog.addRegion': 'Додати регіон',
'schoolCatalog.offline': 'Підключіться до інтернету для керування шкільними канікулами.',
'schoolCatalog.loadError': 'Не вдалося завантажити регіони. Відкрийте налаштування знову.',
'schoolCatalog.retry': 'Спробувати ще раз',
'vacay.subtitle': 'Плануйте й керуйте днями відпустки',
'vacay.settings': 'Налаштування',
'vacay.year': 'Рік',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': 'Thay đổi đã được lưu nhưng không thể làm mới danh sách. Hãy thử tải lại.',
'schoolCatalog.title': 'Kỳ nghỉ học',
'schoolCatalog.hint': 'Quản lý các khu vực dùng chung. Mọi người có thể chọn trong cài đặt nghỉ phép.',
'schoolCatalog.periodHint': 'Bao gồm cả hai ngày. Thêm các khoảng nghỉ cho mỗi năm học.',
'schoolCatalog.region': 'Khu vực hoặc học khu',
'schoolCatalog.name': 'Tên kỳ nghỉ',
'schoolCatalog.start': 'Ngày bắt đầu',
'schoolCatalog.end': 'Ngày kết thúc',
'schoolCatalog.addPeriod': 'Thêm kỳ nghỉ',
'schoolCatalog.invalid': 'Kiểm tra tên và ngày.',
'schoolCatalog.discard': 'Hủy các thay đổi chưa lưu?',
'schoolCatalog.country': 'Quốc gia',
'schoolCatalog.deleteHint': 'Xóa khu vực trước. Không thể xóa khu vực đang được sử dụng.',
'schoolCatalog.deleteCountry': 'Xóa quốc gia',
'schoolCatalog.addCountry': 'Thêm quốc gia',
'schoolCatalog.code': 'Mã quốc gia (ví dụ: US)',
'schoolCatalog.empty': 'Chưa có khu vực.',
'schoolCatalog.addRegion': 'Thêm khu vực',
'schoolCatalog.offline': 'Kết nối internet để quản lý kỳ nghỉ học.',
'schoolCatalog.loadError': 'Không thể tải khu vực. Hãy mở lại cài đặt.',
'schoolCatalog.retry': 'Thử lại',
'vacay.subtitle': 'Lập kế hoạch và quản lý ngày nghỉ',
'vacay.settings': 'Cài đặt',
'vacay.year': 'Năm',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': '變更已儲存,但無法重新整理清單。請重試載入。',
'schoolCatalog.title': '學校假期',
'schoolCatalog.hint': '管理共用地區。所有使用者都可以在休假設定中選擇。',
'schoolCatalog.periodHint': '包含開始與結束當天。請為每個學年新增假期。',
'schoolCatalog.region': '地區或學區',
'schoolCatalog.name': '假期名稱',
'schoolCatalog.start': '開始日期',
'schoolCatalog.end': '結束日期',
'schoolCatalog.addPeriod': '新增假期',
'schoolCatalog.invalid': '請檢查名稱與日期。',
'schoolCatalog.discard': '捨棄尚未儲存的變更?',
'schoolCatalog.country': '國家',
'schoolCatalog.deleteHint': '請先刪除地區。使用中的地區無法刪除。',
'schoolCatalog.deleteCountry': '刪除國家',
'schoolCatalog.addCountry': '新增國家',
'schoolCatalog.code': '國家代碼(如 US)',
'schoolCatalog.empty': '尚無地區。',
'schoolCatalog.addRegion': '新增地區',
'schoolCatalog.offline': '請連接網際網路以管理學校假期。',
'schoolCatalog.loadError': '無法載入地區。請重新開啟設定。',
'schoolCatalog.retry': '重試',
'vacay.subtitle': '規劃和管理假期',
'vacay.settings': '設定',
'vacay.year': '年份',
+21
View File
@@ -1,6 +1,27 @@
import type { TranslationStrings } from '../types';
const vacay: TranslationStrings = {
'schoolCatalog.refreshError': '更改已保存,但无法刷新列表。请重试加载。',
'schoolCatalog.title': '学校假期',
'schoolCatalog.hint': '管理共享地区。所有用户都可以在休假设置中选择。',
'schoolCatalog.periodHint': '包含开始和结束当天。请为每个学年添加假期。',
'schoolCatalog.region': '地区或学区',
'schoolCatalog.name': '假期名称',
'schoolCatalog.start': '开始日期',
'schoolCatalog.end': '结束日期',
'schoolCatalog.addPeriod': '添加假期',
'schoolCatalog.invalid': '请检查名称和日期。',
'schoolCatalog.discard': '放弃未保存的更改?',
'schoolCatalog.country': '国家',
'schoolCatalog.deleteHint': '请先删除地区。正在使用的地区无法删除。',
'schoolCatalog.deleteCountry': '删除国家',
'schoolCatalog.addCountry': '添加国家',
'schoolCatalog.code': '国家代码(如 US)',
'schoolCatalog.empty': '尚无地区。',
'schoolCatalog.addRegion': '添加地区',
'schoolCatalog.offline': '请连接互联网以管理学校假期。',
'schoolCatalog.loadError': '无法加载地区。请重新打开设置。',
'schoolCatalog.retry': '重试',
'vacay.subtitle': '规划和管理假期',
'vacay.settings': '设置',
'vacay.year': '年份',
+2
View File
@@ -75,3 +75,5 @@ export * from './i18n/languages';
export * from './plugin-permissions';
// Plugin settings contracts: the settings-field descriptor + admin instance-config wire shapes.
export * from './plugins/plugins.schema';
export * from './vacay/school-holiday-catalog.schema';
@@ -0,0 +1,47 @@
import {
manualSchoolRegionId,
schoolHolidayCountryRequestSchema,
schoolHolidayPeriodSchema,
schoolHolidayRegionRequestSchema,
} from './school-holiday-catalog.schema';
import { describe, expect, it } from 'vitest';
describe('manual school holiday contracts', () => {
it('accepts a named break spanning New Year and leap days', () => {
expect(
schoolHolidayPeriodSchema.parse({ name: ' Winter ', startDate: '2026-12-20', endDate: '2027-01-06' }).name,
).toBe('Winter');
expect(
schoolHolidayPeriodSchema.safeParse({ name: 'Leap day', startDate: '2028-02-29', endDate: '2028-02-29' }).success,
).toBe(true);
});
it.each([
['2026-02-29', '2026-03-01'],
['2026-04-31', '2026-05-01'],
['2026-12-20', '2026-12-19'],
['2026-01-01', '2028-01-01'],
])('rejects invalid or unbounded dates %s to %s', (startDate, endDate) => {
expect(schoolHolidayPeriodSchema.safeParse({ name: 'Break', startDate, endDate }).success).toBe(false);
});
it('requires a country code, names, revision and bounded periods', () => {
expect(schoolHolidayCountryRequestSchema.safeParse({ code: '../', name: 'USA' }).success).toBe(false);
expect(schoolHolidayCountryRequestSchema.safeParse({ code: 'US', name: ' ' }).success).toBe(false);
expect(schoolHolidayRegionRequestSchema.safeParse({ name: 'District', revision: 0, holidays: [] }).success).toBe(
true,
);
expect(schoolHolidayRegionRequestSchema.safeParse({ name: 'District', holidays: [] }).success).toBe(false);
expect(
schoolHolidayRegionRequestSchema.safeParse({
name: 'District',
revision: 0,
holidays: Array(501).fill({ name: 'Break', startDate: '2026-01-01', endDate: '2026-01-02' }),
}).success,
).toBe(false);
});
it('keeps manual ids distinct from provider subdivisions', () => {
expect(manualSchoolRegionId('US-MANUAL-12')).toBe(12);
for (const code of ['US-WA', 'NL|group:NL-NO', 'US-MANUAL-0', 'US-MANUAL-1junk'])
expect(manualSchoolRegionId(code)).toBeNull();
});
});
@@ -0,0 +1,52 @@
import { idSchema, nonEmptyString } from '../common/primitives.schema';
import { z } from 'zod';
export const schoolHolidayCountryRequestSchema = z.object({
code: z.string().regex(/^[A-Z]{2}$/),
name: nonEmptyString.max(100),
});
export const schoolHolidayPeriodSchema = z
.object({
name: nonEmptyString.max(150),
startDate: z.string().date(),
endDate: z.string().date(),
})
.refine((period) => period.endDate >= period.startDate, {
message: 'End date must be on or after start date',
path: ['endDate'],
})
.refine((period) => Date.parse(period.endDate) - Date.parse(period.startDate) <= 366 * 86400000, {
message: 'A holiday period must not exceed one year',
path: ['endDate'],
});
export const schoolHolidayRegionRequestSchema = z.object({
name: nonEmptyString.max(150),
revision: z.number().int().nonnegative(),
holidays: z.array(schoolHolidayPeriodSchema).max(500),
});
export const schoolHolidayRegionSchema = z.object({
id: idSchema,
country: z.string(),
name: z.string(),
code: z.string(),
revision: z.number().int(),
});
export const schoolHolidayCatalogSchema = z.object({
countries: z.array(schoolHolidayCountryRequestSchema),
regions: z.array(schoolHolidayRegionSchema),
});
export const schoolHolidayRegionDetailSchema = schoolHolidayRegionSchema.extend({
holidays: z.array(schoolHolidayPeriodSchema),
});
export type SchoolHolidayCountryRequest = z.infer<typeof schoolHolidayCountryRequestSchema>;
export type SchoolHolidayPeriod = z.infer<typeof schoolHolidayPeriodSchema>;
export type SchoolHolidayRegionRequest = z.infer<typeof schoolHolidayRegionRequestSchema>;
export type SchoolHolidayRegion = z.infer<typeof schoolHolidayRegionSchema>;
export type SchoolHolidayCatalog = z.infer<typeof schoolHolidayCatalogSchema>;
export type SchoolHolidayRegionDetail = z.infer<typeof schoolHolidayRegionDetailSchema>;
export function manualSchoolRegionId(code: string): number | null {
const match = /^[A-Z]{2}-MANUAL-([1-9][0-9]*)$/.exec(code);
return match ? Number(match[1]) : null;
}