diff --git a/messages/campus/de.msg b/messages/campus/de.msg index 9a4b384fc..43d544af9 100644 --- a/messages/campus/de.msg +++ b/messages/campus/de.msg @@ -1,5 +1,6 @@ CampusIdentPlaceholder: Vorname.Nachname@campus.lmu.de CampusIdent: Campus-Kennung CampusPassword: Passwort +CampusPasswordPlaceholder: Passwort CampusSubmit: Abschicken CampusInvalidCredentials: Ungültige Logindaten \ No newline at end of file diff --git a/messages/dummy/de.msg b/messages/dummy/de.msg index 5a24922aa..16bd26af5 100644 --- a/messages/dummy/de.msg +++ b/messages/dummy/de.msg @@ -1,2 +1,3 @@ -DummyIdent: Nutzer-Kennung +DummyIdent: Identifikation +DummyIdentPlaceholder: Identifikation DummyNoFormData: Keine Formulardaten empfangen \ No newline at end of file diff --git a/messages/pw-hash/de.msg b/messages/pw-hash/de.msg index 9fb1eb5e4..6a172120b 100644 --- a/messages/pw-hash/de.msg +++ b/messages/pw-hash/de.msg @@ -1,2 +1,4 @@ PWHashIdent: Identifikation -PWHashPassword: Passwort \ No newline at end of file +PWHashIdentPlaceholder: Identifikation +PWHashPassword: Passwort +PWHashPasswordPlaceholder: Passwort \ No newline at end of file diff --git a/messages/uniworx/de-de-formal.msg b/messages/uniworx/de-de-formal.msg index 1ca265cc8..a32e40c30 100644 --- a/messages/uniworx/de-de-formal.msg +++ b/messages/uniworx/de-de-formal.msg @@ -73,9 +73,9 @@ Term: Semester TermPlaceholder: W/S + vierstellige Jahreszahl TermStartDay: Erster Tag -TermStartDayTooltip: Üblicherweise immer 1.April oder 1.Oktober +TermStartDayTooltip: Üblicherweise immer 1. April oder 1. Oktober TermEndDay: Letzter Tag -TermEndDayTooltip: Üblicherweise immer 30.September oder 31.März +TermEndDayTooltip: Üblicherweise immer 30. September oder 31. März TermHolidays: Feiertage TermHolidayPlaceholder: Feiertag TermLectureStart: Beginn Vorlesungen @@ -111,8 +111,8 @@ CourseTutorial: Tutorium CourseSecretWrong: Falsches Passwort CourseSecret: Zugangspasswort CourseEditOk tid@TermId ssh@SchoolId csh@CourseShorthand: Kurs #{tid}-#{ssh}-#{csh} wurde erfolgreich geändert. -CourseNewDupShort tid@TermId ssh@SchoolId csh@CourseShorthand: Kurs #{tid}-#{ssh}-#{csh} konnte nicht erstellt werden: Es gibt bereits einen anderen Kurs mit dem Kürzel #{csh} in diesem Semester. -CourseEditDupShort tid@TermId ssh@SchoolId csh@CourseShorthand: Kurs #{tid}-#{ssh}-#{csh} konnte nicht geändert werden: Es gibt bereits einen anderen Kurs mit dem Kürzel #{csh} in diesem Semester. +CourseNewDupShort tid@TermId ssh@SchoolId csh@CourseShorthand: Kurs #{tid}-#{ssh}-#{csh} konnte nicht erstellt werden: Es gibt bereits einen anderen Kurs mit dem Kürzel #{csh} in diesem Semester und Institut. +CourseEditDupShort tid@TermId ssh@SchoolId csh@CourseShorthand: Kurs #{tid}-#{ssh}-#{csh} konnte nicht geändert werden: Es gibt bereits einen anderen Kurs mit dem Kürzel #{csh} in diesem Semester und Institut. FFSheetName: Name TermCourseListHeading tid@TermId: Kursübersicht #{tid} TermSchoolCourseListHeading tid@TermId school@SchoolName: Kursübersicht #{tid} für #{school} @@ -130,7 +130,7 @@ CourseMembersCountLimited n@Int max@Int: #{n}/#{max} CourseMembersCountOf n@Int mbNum@IntMaybe: #{n} Kursanmeldungen #{maybeToMessage " von " mbNum " möglichen"} CourseName: Name CourseDescription: Beschreibung -CourseDescriptionTip: Beliebiges HTML-Markup ist gestattet +CourseDescriptionTip: Beliebiges Html-Markup ist gestattet CourseHomepageExternal: Externe Homepage CourseShorthand: Kürzel CourseShorthandUnique: Muss nur innerhalb Institut und Semester eindeutig sein. Wird verbatim in die Url der Kursseite übernommen. @@ -274,6 +274,8 @@ SheetSubmissionMode: Abgabe-Modus SheetExercise: Aufgabenstellung SheetHint: Hinweis SheetHintFrom: Hinweis ab +SheetHintFromPlaceholder: Datum, sonst nur für Korrektoren +SheetSolutionFromPlaceholder: Datum, sonst nur für Korrektoren SheetSolution: Lösung SheetSolutionFrom: Lösung ab SheetMarking: Hinweise für Korrektoren @@ -2060,4 +2062,35 @@ ProfileSubmissions: Abgaben ProfileRemark: Hinweis ProfileGroupSubmissionDates: Bei Gruppenabgaben wird kein Datum angezeigt, wenn Sie die Gruppenabgabe nie selbst hochgeladen haben. ProfileCorrectorRemark: Die oberhalb angezeigte Tabelle zeigt nur prinzipielle Einteilungen als Korrektor zu einem Übungsblatt. Auch ohne Einteilung können Korrekturen einzeln zugewiesen werden, welche hier dann nicht aufgeführt werden. -ProfileCorrections: Auflistung aller zugewiesenen Korrekturen \ No newline at end of file +ProfileCorrections: Auflistung aller zugewiesenen Korrekturen + +GroupSizeNotNatural: „Gruppengröße“ muss eine natürliche Zahl sein +AmbiguousEmail: E-Mail Adresse nicht eindeutig +CourseDescriptionPlaceholder: Bitte mindestens die Modulbeschreibung angeben +CourseHomepageExternalPlaceholder: Optionale externe URL +PointsPlaceholder: Punktezahl +RFC1766: RFC1766-Sprachcode + +TermShort: Kürzel +TermCourseCount: Kurse +TermStart: Semesteranfang +TermEnd: Semesterende +TermStartMustMatchName: Jahreszahl im Namenskürzel stimmt nicht mit Semesterbeginn überein. +TermEndMustBeAfterStart: Semester darf nicht enden, bevor es beginnt. +TermLectureEndMustBeAfterStart: Vorlesungszeit muss vor ihrem Ende anfgangen. +TermStartMustBeBeforeLectureStart: Semester muss vor der Vorlesungszeit beginnen. +TermEndMustBeAfterLectureEnd: Vorlesungszeit muss vor dem Semester enden. +AdminPageEmpty: Diese Seite soll eine Übersichtsseite für Administratoren werden. Aktuell finden sich hier nur Links zu wichtigen Administrator-Funktionalitäten. +HaveCorrectorAccess sheetName@SheetName: Sie haben Korrektor-Zugang zu #{original sheetName}. +FavouritesPlaceholder: Anzahl Favoriten +FavouritesNotNatural: Anzahl der Favoriten muss eine natürliche Zahl sein! +FavouritesSemestersPlaceholder: Anzahl Semester +FavouritesSemestersNotNatural: Anzahl der Favoriten-Semester muss eine natürliche Zahl sein! + +ProfileTitle: Benutzereinstellungen + +GlossaryTitle: Begriffsverzeichnis +MenuGlossary: Begriffsverzeichnis + +Applicant: Bewerber +CourseParticipant: Kursteilnehmer \ No newline at end of file diff --git a/messages/uniworx/en-eu.msg b/messages/uniworx/en-eu.msg index c6e1dc4ad..a04d6b359 100644 --- a/messages/uniworx/en-eu.msg +++ b/messages/uniworx/en-eu.msg @@ -1,2 +1,146 @@ +Logo: Uni2work + +BtnSubmit: Submit +BtnAbort: Abort +BtnDelete: Delete +BtnRegister: Register +BtnDeregister: Deregister +BtnCourseRegister: Enrol for course +BtnCourseDeregister: Leave course +BtnCourseApply: Apply for course +BtnCourseRetractApplication: Retract application +BtnExamRegister: Enrol for exam +BtnExamDeregister: Leave exam +BtnHijack: Hijack session +BtnSave: Save +PressSaveToSave: Changes will only be saved after clicking "Save". +BtnHandIn: Hand in submission +BtnCandidatesInfer: Infer mapping +BtnCandidatesDeleteConflicts: Delete conflicts +BtnCandidatesDeleteAll: Delete all observations +BtnResetTokens: Invalidate tokens +BtnLecInvAccept: Accept +BtnLecInvDecline: Decline +BtnCorrInvAccept: Accept +BtnCorrInvDecline: Decline +BtnSubmissionsAssign: Assign submissions automatically + +Aborted: Aborted +Remarks: Remarks +Registered: Registered +RegisteredSince: Registered since +NotRegistered: Note enrolled for this course +Registration: Registration +RegisterFrom: Registration starts +RegisterTo: Registration ends +DeRegUntil: Deregistration until +RegisterRetry: You haven't been enrolled. Press "Enrol for course" to enrol + +CourseRegistrationInterval: Registration +CourseDirectRegistrationInterval: Direct registration +CourseDeregisterUntil time: Deregistration only until #{time} + +GenericKey: Key +GenericShort: Shorthand +GenericIsNew: New +GenericHasConflict: Conflict +GenericBack: Back +GenericChange: Change +GenericNumChange: +/- +GenericMin: Min +GenericAvg: Avg +GenericMax: Max +GenericAll: All + +SummerTerm year: Summer semester #{year} +WinterTerm year: Winter semester #{year}/#{succ year} +SummerTermShort year: Summer #{year} +WinterTermShort year: Winter #{year}/#{mod (succ year) 100} +PSLimitNonPositive: “pagesize” must be greater than zero +Page num: #{num} + +TermsHeading: Semesters +TermCurrent: Current semester +TermEditHeading: Edit semester +TermEditTid tid: Edit semester #{tid} +TermEdited tid: Successfully edited semester #{tid} +TermNewTitle: Edit/create semester +InvalidInput: Invalid input +Term: Semester +TermPlaceholder: (W|S) + +TermStartDay: Starting day +TermStartDayTooltip: Usually 1st of April or 1st of October +TermEndDay: Last day +TermEndDayTooltip: Usually 30th of September or 31st of March +TermHolidays: Legal holidays +TermHolidayPlaceholder: Legal holiday +TermLectureStart: Lectures start +TermLectureEnd: Lectures end +TermLectureEndTooltip: Summer semesters are usually 14 weeks; winter semesters 15 +TermActive: Active + +SchoolListHeading: Departments +SchoolHeading school: #{school} + +LectureStart: Lectures start + +Course: Course +CourseShort: Shorthand +CourseCapacity: Capacity +CourseCapacityTip: Maximum permissable number of enrollments for this course; leave empty for unlimited capacity +CourseNoCapacity: Course has reached maximum capacity +TutorialNoCapacity: Tutorial has reached maximum capacity +CourseNotEmpty: There are currently no participants enrolled for this course. +CourseRegistration: Registration +CourseRegisterOpen: Registration is open +CourseRegisterOk: Successfully enrolled for course +CourseDeregisterOk: Successfully left course +CourseApplyOk: Successfully applied for course +CourseRetractApplyOk: Successfully retracted application for course +CourseDeregisterLecturerTip: If you deregister the participant you might loose access to this data +CourseStudyFeature: Associated subject +CourseStudyFeatureTip: For information purposes only (visible to course administrators) +CourseStudyFeatureUpdated: Successfully updated associated subject +CourseStudyFeatureNone: No associated subject +CourseTutorial: Tutorial +CourseSecretWrong: Wrong password +CourseSecret: Access password +CourseEditOk tid ssh csh: Successfully edited course #{tid}-#{ssh}-#{csh} +CourseNewDupShort tid ssh csh: Could not create course #{tid}-#{ssh}-#{csh}. Another course with shorthand #{csh} already exists for the given semester and school. +CourseEditDupShort tid ssh csh: Could not edit course #{tid}-#{ssh}-#{csh}. Another course with shorthand #{csh} already exists for the given semester and school. +FFSheetName: Name +TermCourseListHeading tid: Courses #{tid} +TermSchoolCourseListHeading tid school: Courses #{tid}, #{school} +CourseListTitle: All courses +TermCourseListTitle tid: Courses #{tid} +TermSchoolCourseListTitle tid school: Courses #{tid}, #{school} +CourseNewHeading: Create new course +CourseEditHeading tid ssh csh: Edit course #{tid}-#{ssh}-#{csh} +CourseEditTitle: Edit/Create course +CourseMembers: Participants +CourseMemberOf: Participant of +CourseAssociatedWith: associated with +CourseMembersCount n: #{n} +CourseMembersCountLimited n max: #{n}/#{max} +CourseMembersCountOf n mbNum: #{n} #{maybeToMessage "of " mbNum " "}participants +CourseName: Title +CourseDescription: Description +CourseDescriptionTip: You may use arbitrary Html-Markup +CourseHomepageExternal: External homepage +CourseShorthand: Shorthand +CourseShorthandUnique: Needs to be unique within school and semester. Will be used verbatim within the url of the course page. +CourseSemester: Semester +CourseSchool: Department +CourseSchoolShort: Department +CourseSecretTip: Enrollment for this course will require the password, if set +CourseSecretFormat: Arbitrary string +CourseRegisterFromTip: When left empty students will not be able to enroll themselves +CourseRegisterToTip: May be left empty to allow enrollment indefinitely +CourseDeregisterUntilTip: Participants may deregister from immediately after registration starts up to this time. May be left empty to allow deregistration indefinitely. +CourseFilterSearch: Text search +CourseFilterRegistered: Registered + + MenuLanguage: Language LanguageChanged: Language changed successfully \ No newline at end of file diff --git a/routes b/routes index b64e379b5..1c5500267 100644 --- a/routes +++ b/routes @@ -63,6 +63,7 @@ /info/lecturer InfoLecturerR GET !lecturer /info/data DataProtR GET !free /info/allocation InfoAllocationR GET !free +/info/glossary GlossaryR GET !free /impressum ImpressumR GET !free /version VersionR GET !free diff --git a/src/Auth/Dummy.hs b/src/Auth/Dummy.hs index 4bfc09d01..9f88b5ed6 100644 --- a/src/Auth/Dummy.hs +++ b/src/Auth/Dummy.hs @@ -13,6 +13,7 @@ import qualified Data.CaseInsensitive as CI data DummyMessage = MsgDummyIdent + | MsgDummyIdentPlaceholder | MsgDummyNoFormData deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable) @@ -24,7 +25,9 @@ dummyForm :: ( RenderMessage (HandlerSite m) FormMessage , Button (HandlerSite m) ButtonSubmit , MonadHandler m ) => AForm m (CI Text) -dummyForm = areq (ciField & addDatalist userList) (fslI MsgDummyIdent & noAutocomplete) Nothing +dummyForm = wFormToAForm $ do + mr <- getMessageRender + aFormToWForm $ areq (ciField & addDatalist userList) (fslpI MsgDummyIdent (mr MsgDummyIdentPlaceholder) & noAutocomplete) Nothing where userList = fmap mkOptionList . runDB $ withReaderT projectBackend (map toOption <$> selectList [] [Asc UserIdent] :: ReaderT SqlBackend _ [Option UserIdent]) toOption (Entity _ User{..}) = Option userDisplayName userIdent (CI.original userIdent) diff --git a/src/Auth/LDAP.hs b/src/Auth/LDAP.hs index 7863ec34a..e35bfce1b 100644 --- a/src/Auth/LDAP.hs +++ b/src/Auth/LDAP.hs @@ -36,6 +36,7 @@ data CampusLogin = CampusLogin data CampusMessage = MsgCampusIdentPlaceholder | MsgCampusIdent | MsgCampusPassword + | MsgCampusPasswordPlaceholder | MsgCampusSubmit | MsgCampusInvalidCredentials deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable) @@ -129,7 +130,7 @@ campusForm = do MsgRenderer mr <- getMsgRenderer ident <- wreq ciField (fslpI MsgCampusIdent (mr MsgCampusIdentPlaceholder) & addAttr "autofocus" "") Nothing - password <- wreq passwordField (fslI MsgCampusPassword) Nothing + password <- wreq passwordField (fslpI MsgCampusPassword (mr MsgCampusPasswordPlaceholder)) Nothing return $ CampusLogin <$> ident diff --git a/src/Auth/PWHash.hs b/src/Auth/PWHash.hs index b2194bf90..3fd716694 100644 --- a/src/Auth/PWHash.hs +++ b/src/Auth/PWHash.hs @@ -22,7 +22,9 @@ data HashLogin = HashLogin } deriving (Generic, Typeable) data PWHashMessage = MsgPWHashIdent + | MsgPWHashIdentPlaceholder | MsgPWHashPassword + | MsgPWHashPasswordPlaceholder deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable) @@ -30,9 +32,11 @@ hashForm :: ( RenderMessage (HandlerSite m) FormMessage , RenderMessage (HandlerSite m) PWHashMessage , MonadHandler m ) => AForm m HashLogin -hashForm = HashLogin - <$> areq ciField (fslpI MsgPWHashIdent "Identifikation") Nothing - <*> areq passwordField (fslpI MsgPWHashPassword "Passwort") Nothing +hashForm = wFormToAForm $ do + mr <- getMessageRender + aFormToWForm $ HashLogin + <$> areq ciField (fslpI MsgPWHashIdent (mr MsgPWHashIdentPlaceholder)) Nothing + <*> areq passwordField (fslpI MsgPWHashPassword (mr MsgPWHashPasswordPlaceholder)) Nothing hashLogin :: forall site. diff --git a/src/Foundation.hs b/src/Foundation.hs index 1b17acd0c..ffe945263 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -2182,7 +2182,9 @@ instance YesodBreadcrumbs UniWorX where if | mayList -> i18nCrumb MsgBreadcrumbSystemMessage $ Just MessageListR | otherwise -> i18nCrumb MsgBreadcrumbSystemMessage $ Just HomeR - breadcrumb (MessageListR) = i18nCrumb MsgMenuMessageList $ Just AdminR + breadcrumb MessageListR = i18nCrumb MsgMenuMessageList $ Just AdminR + + breadcrumb GlossaryR = i18nCrumb MsgMenuGlossary $ Just InfoR -- breadcrumb _ = return ("Uni2work", Nothing) -- Default is no breadcrumb at all submissionList :: TermId -> CourseShorthand -> SheetName -> UserId -> DB [E.Value SubmissionId] @@ -2480,6 +2482,14 @@ pageActions (InfoR) = [ , menuItemModal = False , menuItemAccessCallback' = return True } + , MenuItem + { menuItemType = PageActionPrime + , menuItemLabel = MsgMenuGlossary + , menuItemIcon = Nothing + , menuItemRoute = SomeRoute GlossaryR + , menuItemModal = False + , menuItemAccessCallback' = return True + } ] pageActions (VersionR) = [ MenuItem diff --git a/src/Handler/Admin.hs b/src/Handler/Admin.hs index 5bcf4359a..740609be0 100644 --- a/src/Handler/Admin.hs +++ b/src/Handler/Admin.hs @@ -33,10 +33,7 @@ getAdminR :: Handler Html getAdminR = -- do siteLayoutMsg MsgAdminHeading $ do setTitleI MsgAdminHeading - [whamlet| - This shall become the Administrators' overview page. - Its current purpose is to provide links to some important admin functions - |] + i18n MsgAdminPageEmpty -- BEGIN - Buttons needed only here data ButtonCreate = CreateMath | CreateInf -- Dummy for Example @@ -74,7 +71,7 @@ emailTestForm = (,) makeDemoForm :: Int -> Form (Int,Bool,Double) makeDemoForm n = identifyForm ("adminTestForm" :: Text) $ \html -> do (result, widget) <- flip (renderAForm FormStandard) html $ (,,) - <$> areq (minIntField n "Zahl") (fromString $ "Ganzzahl > " ++ show n) Nothing + <$> areq (minIntFieldI n ("Zahl" :: Text)) (fromString $ "Ganzzahl > " ++ show n) Nothing <* aformSection MsgFormBehaviour <*> areq checkBoxField "Muss nächste Zahl größer sein?" (Just True) <*> areq doubleField "Fliesskommazahl" Nothing @@ -254,8 +251,9 @@ postAdminTestR = do getAdminErrMsgR, postAdminErrMsgR :: Handler Html getAdminErrMsgR = postAdminErrMsgR postAdminErrMsgR = do + MsgRenderer mr <- getMsgRenderer ((ctResult, ctView), ctEncoding) <- runFormPost . renderAForm FormStandard $ - unTextarea <$> areq textareaField (fslpI MsgErrMsgCiphertext "Ciphertext") Nothing + unTextarea <$> areq textareaField (fslpI MsgErrMsgCiphertext (mr MsgErrMsgCiphertext)) Nothing plaintext <- formResultMaybe ctResult $ exceptT (\err -> Nothing <$ addMessageI Error err) (return . Just) . (encodedSecretBoxOpen :: Text -> ExceptT EncodedSecretBoxException Handler Value) diff --git a/src/Handler/Corrections.hs b/src/Handler/Corrections.hs index 9b3937a5d..b9e16b4bb 100644 --- a/src/Handler/Corrections.hs +++ b/src/Handler/Corrections.hs @@ -714,13 +714,14 @@ postCorrectionR tid ssh csh shn cid = do results <- runDB $ correctionData tid ssh csh shn sub + MsgRenderer mr <- getMsgRenderer case results of [(Entity _ Course{..}, Entity _ Sheet{..}, Entity _ subm@Submission{..}, corrector)] -> do let ratingComment = fmap Text.strip submissionRatingComment >>= (\c -> c <$ guard (not $ null c)) pointsForm = case sheetType of NotGraded -> pure Nothing _otherwise -> aopt (pointsFieldMax $ preview (_grading . _maxPoints) sheetType) - (fslpI MsgRatingPoints "Punktezahl" & setTooltip sheetType) + (fslpI MsgRatingPoints (mr MsgPointsPlaceholder) & setTooltip sheetType) (Just submissionRatingPoints) ((corrResult, corrForm'), corrEncoding) <- runFormPost . identifyForm FIDcorrection . renderAForm FormStandard $ (,,) @@ -776,7 +777,6 @@ postCorrectionR tid ssh csh shn cid = do addMessageI Success MsgRatingFilesUpdated redirect $ CSubmissionR tid ssh csh shn cid CorrectionR - mr <- getMessageRender let sheetTypeDesc = mr sheetType heading = MsgCorrectionHead tid ssh csh shn cid headingWgt = [whamlet| @@ -868,9 +868,10 @@ postCorrectionsCreateR = do , optionInternalValue = sid , optionExternalValue = toPathPiece (cID :: CryptoUUIDSheet) } + MsgRenderer mr <- getMsgRenderer ((pseudonymRes, pseudonymWidget), pseudonymEncoding) <- runFormPost . renderAForm FormStandard $ (,) <$> areq (selectField sheetOptions) (fslI MsgPseudonymSheet) Nothing - <*> (textToList <$> areq textareaField (fslpI MsgCorrectionPseudonyms "Pseudonyme" & setTooltip MsgCorrectionPseudonymsTip) Nothing) + <*> (textToList <$> areq textareaField (fslpI MsgCorrectionPseudonyms (mr MsgPseudonyms) & setTooltip MsgCorrectionPseudonymsTip) Nothing) case pseudonymRes of FormMissing -> return () diff --git a/src/Handler/Course/Edit.hs b/src/Handler/Course/Edit.hs index 81dbe8573..b8fcf5748 100644 --- a/src/Handler/Course/Edit.hs +++ b/src/Handler/Course/Edit.hs @@ -256,7 +256,6 @@ makeCourseForm miButtonAction template = identifyForm FIDcourse . validateFormDB optionalActionW' (bool mforcedJust mpopt mayChange) allocationForm' (fslI MsgCourseAllocationParticipate & setTooltip MsgCourseAllocationParticipateTip) (is _Just . cfAllocation <$> template) - -- TODO: internationalization -- let autoUnzipInfo = [|Entpackt hochgeladene Zip-Dateien (*.zip) automatisch und fügt den Inhalt dem Stamm-Verzeichnis der Abgabe hinzu. TODO|] (result, widget) <- flip (renderAForm FormStandard) html $ CourseForm @@ -267,9 +266,9 @@ makeCourseForm miButtonAction template = identifyForm FIDcourse . validateFormDB & setTooltip MsgCourseShorthandUnique) (cfShort <$> template) <*> areq (schoolFieldFor userSchools) (fslI MsgCourseSchool) (cfSchool <$> template) <*> areq termsField (fslI MsgCourseSemester) (cfTerm <$> template) - <*> aopt htmlField (fslpI MsgCourseDescription "Bitte mindestens die Modulbeschreibung angeben" + <*> aopt htmlField (fslpI MsgCourseDescription (mr MsgCourseDescriptionPlaceholder) & setTooltip MsgCourseDescriptionTip) (cfDesc <$> template) - <*> aopt (urlField & cfStrip) (fslpI MsgCourseHomepageExternal "Optionale externe URL") + <*> aopt (urlField & cfStrip) (fslpI MsgCourseHomepageExternal (mr MsgCourseHomepageExternalPlaceholder)) (cfLink <$> template) <*> apopt checkBoxField (fslI MsgMaterialFree) (cfMatFree <$> template) <* aformSection MsgCourseFormSectionRegistration diff --git a/src/Handler/Info.hs b/src/Handler/Info.hs index 751bbe171..c4a5fe7dc 100644 --- a/src/Handler/Info.hs +++ b/src/Handler/Info.hs @@ -3,6 +3,9 @@ module Handler.Info where import Import import Handler.Utils +import qualified Data.Map as Map +import qualified Data.CaseInsensitive as CI + import Development.GitRev -- | Versionsgeschichte @@ -67,3 +70,21 @@ getInfoLecturerR = if currentTime > expiryTime then mempty else toWidget [whamlet| ^{iconTooltip tooltipNew (Just IconNew) False} |] + +getGlossaryR :: Handler Html +getGlossaryR = + siteLayoutMsg' MsgGlossaryTitle $ do + setTitleI MsgGlossaryTitle + MsgRenderer mr <- getMsgRenderer + let + entries' = sortOn (CI.mk . view _2) $ do + (k, v) <- Map.toList entries + msg <- maybeToList $ Map.lookup k msgMap + return (k, mr msg, v) + $(widgetFile "glossary") + where + entries = $(i18nWidgetFiles "glossary") + msgMap = Map.fromList + [ ("applicant" , MsgApplicant ) + , ("course-participant", MsgCourseParticipant) + ] diff --git a/src/Handler/Profile.hs b/src/Handler/Profile.hs index 170ce5dc4..b68c93006 100644 --- a/src/Handler/Profile.hs +++ b/src/Handler/Profile.hs @@ -79,15 +79,16 @@ instance RenderMessage UniWorX NotificationTriggerKind where makeSettingForm :: Maybe SettingsForm -> Form SettingsForm makeSettingForm template html = do + MsgRenderer mr <- getMsgRenderer (result, widget) <- flip (renderAForm FormStandard) html $ SettingsForm <$ aformSection MsgFormPersonalAppearance <*> areq (textField & cfStrip) (fslI MsgUserDisplayName & setTooltip MsgUserDisplayNameRulesBelow) (stgDisplayName <$> template) <*> areq (emailField & cfStrip & cfCI) (fslI MsgUserDisplayEmail & setTooltip MsgUserDisplayEmailTip) (stgDisplayEmail <$> template) <* aformSection MsgFormCosmetics - <*> areq (natFieldI $ MsgNatField "Favoriten") - (fslpI MsgFavourites "Anzahl Favoriten" & setTooltip MsgFavouritesTip) (stgMaxFavourites <$> template) - <*> areq (natFieldI $ MsgNatField "Favoriten-Semester") - (fslpI MsgFavouriteSemesters "Anzahl Semester") (stgMaxFavouriteTerms <$> template) + <*> areq (natFieldI MsgFavouritesNotNatural) + (fslpI MsgFavourites (mr MsgFavouritesPlaceholder) & setTooltip MsgFavouritesTip) (stgMaxFavourites <$> template) + <*> areq (natFieldI MsgFavouritesSemestersNotNatural) + (fslpI MsgFavouriteSemesters (mr MsgFavouritesSemestersPlaceholder)) (stgMaxFavouriteTerms <$> template) <*> areq (selectField . return $ mkOptionList themeList) (fslI MsgTheme) { fsId = Just "theme-select" } (stgTheme <$> template) <*> areq (selectField $ dateTimeFormatOptions SelFormatDateTime) (fslI MsgDateTimeFormat) (stgDateTime <$> template) @@ -318,7 +319,7 @@ postProfileR = do tResetTime <- traverse (formatTime SelFormatDateTime) userTokensIssuedAfter siteLayout [whamlet|_{MsgProfileFor} ^{nameWidget userDisplayName userSurname}|] $ do - setTitle . toHtml $ "Profil " <> userIdent + setTitleI MsgProfileTitle let settingsForm = wrapForm formWidget FormSettings { formMethod = POST diff --git a/src/Handler/Sheet.hs b/src/Handler/Sheet.hs index 543865af2..caca8b576 100644 --- a/src/Handler/Sheet.hs +++ b/src/Handler/Sheet.hs @@ -91,7 +91,7 @@ makeSheetForm msId template = identifyForm FIDsheet $ \html -> do oldFileIds <- (return.) <$> case msId of Nothing -> return $ partitionFileType mempty (Just sId) -> liftHandler $ runDB $ getFtIdMap sId - mr <- getMsgRenderer + mr'@(MsgRenderer mr) <- getMsgRenderer ctime <- ceilingQuarterHour <$> liftIO getCurrentTime (result, widget) <- flip (renderAForm FormStandard) html $ SheetForm <$> areq (textField & cfStrip & cfCI) (fslI MsgSheetName) (sfName <$> template) @@ -103,9 +103,9 @@ makeSheetForm msId template = identifyForm FIDsheet $ \html -> do & setTooltip MsgSheetActiveFromTip) (sfActiveFrom <$> template) <*> areq utcTimeField (fslI MsgSheetActiveTo) (sfActiveTo <$> template) - <*> aopt utcTimeField (fslpI MsgSheetHintFrom "Datum, sonst nur für Korrektoren" + <*> aopt utcTimeField (fslpI MsgSheetHintFrom (mr MsgSheetHintFromPlaceholder) & setTooltip MsgSheetHintFromTip) (sfHintFrom <$> template) - <*> aopt utcTimeField (fslpI MsgSheetSolutionFrom "Datum, sonst nur für Korrektoren" + <*> aopt utcTimeField (fslpI MsgSheetSolutionFrom (mr MsgSheetSolutionFromPlaceholder) & setTooltip MsgSheetSolutionFromTip) (sfSolutionFrom <$> template) <* aformSection MsgSheetFormFiles <*> aopt (multiFileField $ oldFileIds SheetExercise) (fslI MsgSheetExercise) (sfSheetF <$> template) @@ -124,7 +124,7 @@ makeSheetForm msId template = identifyForm FIDsheet $ \html -> do <*> aopt htmlField (fslpI MsgSheetMarking "Html") (sfMarkingText <$> template) return $ case result of FormSuccess sheetResult - | errorMsgs <- validateSheet mr sheetResult + | errorMsgs <- validateSheet mr' sheetResult , not $ null errorMsgs -> (FormFailure errorMsgs, widget) _ -> (result, widget) @@ -923,5 +923,5 @@ postSCorrInviteR = invitationR correctorInvitationConfig getSIsCorrR :: TermId -> SchoolId -> CourseShorthand -> SheetName -> Handler Html -- NOTE: The route SIsCorrR is only used to verfify corrector access rights to given sheet! getSIsCorrR _ _ _ shn = do - defaultLayout $ [whamlet|You have corrector access to #{shn}.|] + defaultLayout . i18n $ MsgHaveCorrectorAccess shn diff --git a/src/Handler/SystemMessage.hs b/src/Handler/SystemMessage.hs index c6a3c2214..3ae62b70f 100644 --- a/src/Handler/SystemMessage.hs +++ b/src/Handler/SystemMessage.hs @@ -23,6 +23,7 @@ postMessageR cID = do Nothing -> (systemMessageSummary, systemMessageContent) Just SystemMessageTranslation{..} -> (systemMessageTranslationSummary, systemMessageTranslationContent) + MsgRenderer mr <- getMsgRenderer let mkForm = do ((modifyRes, modifyView), modifyEnctype) <- runFormPost . identifyForm FIDSystemMessageModify . renderAForm FormStandard @@ -31,9 +32,9 @@ postMessageR cID = do <*> aopt utcTimeField (fslI MsgSystemMessageTo) (Just systemMessageTo) <*> areq checkBoxField (fslI MsgSystemMessageAuthenticatedOnly) (Just systemMessageAuthenticatedOnly) <*> areq (selectField optionsFinite) (fslI MsgSystemMessageSeverity) (Just systemMessageSeverity) - <*> areq (langField False) (fslpI MsgSystemMessageLanguage "RFC1766-Sprachcode") (Just systemMessageDefaultLanguage) - <*> areq htmlField' (fslpI MsgSystemMessageContent "HTML") (Just systemMessageContent) - <*> aopt htmlField' (fslpI MsgSystemMessageSummary "HTML") (Just systemMessageSummary) + <*> areq (langField False) (fslpI MsgSystemMessageLanguage (mr MsgRFC1766)) (Just systemMessageDefaultLanguage) + <*> areq htmlField' (fslpI MsgSystemMessageContent "Html") (Just systemMessageContent) + <*> aopt htmlField' (fslpI MsgSystemMessageSummary "Html") (Just systemMessageSummary) ts <- runDB $ selectList [ SystemMessageTranslationMessage ==. smId ] [Asc SystemMessageTranslationLanguage] let ts' = Map.fromList $ (systemMessageTranslationLanguage . entityVal &&& id) <$> ts @@ -45,9 +46,9 @@ postMessageR cID = do <$> fmap (Entity tId) ( SystemMessageTranslation <$> pure systemMessageTranslationMessage - <*> areq (langField False) (fslpI MsgSystemMessageLanguage "RFC1766-Sprachcode") (Just systemMessageTranslationLanguage) - <*> areq htmlField' (fslpI MsgSystemMessageContent "HTML") (Just systemMessageTranslationContent) - <*> aopt htmlField' (fslpI MsgSystemMessageSummary "HTML") (Just systemMessageTranslationSummary) + <*> areq (langField False) (fslpI MsgSystemMessageLanguage (mr MsgRFC1766)) (Just systemMessageTranslationLanguage) + <*> areq htmlField' (fslpI MsgSystemMessageContent "Html") (Just systemMessageTranslationContent) + <*> aopt htmlField' (fslpI MsgSystemMessageSummary "Html") (Just systemMessageTranslationSummary) ) <*> combinedButtonFieldF "" @@ -56,9 +57,9 @@ postMessageR cID = do ((addTransRes, addTransView), addTransEnctype) <- runFormPost . identifyForm FIDSystemMessageAddTranslation . renderAForm FormStandard $ SystemMessageTranslation <$> pure smId - <*> areq (langField False) (fslpI MsgSystemMessageLanguage "RFC1766-Sprachcode") Nothing - <*> areq htmlField' (fslpI MsgSystemMessageContent "HTML") Nothing - <*> aopt htmlField' (fslpI MsgSystemMessageSummary "HTML") Nothing + <*> areq (langField False) (fslpI MsgSystemMessageLanguage (mr MsgRFC1766)) Nothing + <*> areq htmlField' (fslpI MsgSystemMessageContent "Html") Nothing + <*> aopt htmlField' (fslpI MsgSystemMessageSummary "Html") Nothing formResult modifyRes $ modifySystemMessage smId @@ -252,14 +253,15 @@ postMessageListR = do FormSuccess (_, _selection) -- prop> null _selection -> addMessageI Error MsgSystemMessageEmptySelection + MsgRenderer mr <- getMsgRenderer ((addRes, addView), addEncoding) <- runFormPost . identifyForm FIDSystemMessageAdd . renderAForm FormStandard $ SystemMessage <$> aopt utcTimeField (fslI MsgSystemMessageFrom) Nothing <*> aopt utcTimeField (fslI MsgSystemMessageTo) Nothing <*> areq checkBoxField (fslI MsgSystemMessageAuthenticatedOnly) Nothing <*> areq (selectField optionsFinite) (fslI MsgSystemMessageSeverity) Nothing - <*> areq (langField False) (fslpI MsgSystemMessageLanguage "RFC1766-Sprachcode") (Just $ NonEmpty.head appLanguages) - <*> areq htmlField' (fslpI MsgSystemMessageContent "HTML") Nothing - <*> aopt htmlField' (fslpI MsgSystemMessageSummary "HTML") Nothing + <*> areq (langField False) (fslpI MsgSystemMessageLanguage (mr MsgRFC1766)) (Just $ NonEmpty.head appLanguages) + <*> areq htmlField' (fslpI MsgSystemMessageContent "Html") Nothing + <*> aopt htmlField' (fslpI MsgSystemMessageSummary "Html") Nothing case addRes of FormMissing -> return () diff --git a/src/Handler/Term.hs b/src/Handler/Term.hs index 26c349c83..abce61c63 100644 --- a/src/Handler/Term.hs +++ b/src/Handler/Term.hs @@ -7,6 +7,8 @@ import qualified Data.Map as Map import qualified Database.Esqueleto as E import qualified Data.Set as Set + +import qualified Control.Monad.State.Class as State -- | Default start day of term for season, @@ -18,32 +20,15 @@ defaultDay True Summer = fromGregorian 2020 4 1 defaultDay False Summer = fromGregorian 2020 9 30 -validateTerm :: Term -> [Text] -validateTerm Term{..} = - [ msg | (False, msg) <- - [ --startOk - ( termStart `withinTerm` termName - , "Jahreszahl im Namenskürzel stimmt nicht mit Semesterbeginn überein." - ) - , -- endOk - ( termStart < termEnd - , "Semester darf nicht enden, bevor es begann." - ) - , -- startOk - ( termLectureStart < termLectureEnd - , "Vorlesungszeit muss vor ihrem Ende anfgangen." - ) - , -- lecStartOk - ( termStart <= termLectureStart - , "Semester muss vor der Vorlesungszeit beginnen." - ) - , -- lecEndOk - ( termEnd >= termLectureEnd - , "Vorlesungszeit muss vor dem Semester enden." - ) - ] ] - - +validateTerm :: (MonadHandler m, HandlerSite m ~ UniWorX) + => FormValidator Term m () +validateTerm = do + Term{..} <- State.get + guardValidation MsgTermStartMustMatchName $ termStart `withinTerm` termName + guardValidation MsgTermEndMustBeAfterStart $ termStart < termEnd + guardValidation MsgTermLectureEndMustBeAfterStart $ termLectureStart < termLectureEnd + guardValidation MsgTermStartMustBeBeforeLectureStart $ termStart <= termLectureStart + guardValidation MsgTermEndMustBeAfterLectureEnd $ termEnd >= termLectureEnd getTermShowR :: Handler TypedContent @@ -66,22 +51,22 @@ getTermShowR = do provideRep $ toJSON . map fst <$> runDB (E.select $ E.from termData) provideRep $ do let colonnadeTerms = widgetColonnade $ mconcat - [ sortable (Just "term-id") "Kürzel" $ \(Entity tid _, _) -> anchorCell + [ sortable (Just "term-id") (i18nCell MsgTermShort) $ \(Entity tid _, _) -> anchorCell (TermCourseListR tid) [whamlet|#{toPathPiece tid}|] , sortable (Just "lecture-start") (i18nCell MsgLectureStart) $ \(Entity _ Term{..},_) -> cell $ formatTime SelFormatDate termLectureStart >>= toWidget - , sortable (Just "lecture-end") "Ende Vorlesungen" $ \(Entity _ Term{..},_) -> + , sortable (Just "lecture-end") (i18nCell MsgTermLectureEnd) $ \(Entity _ Term{..},_) -> cell $ formatTime SelFormatDate termLectureEnd >>= toWidget - , sortable Nothing "Aktiv" $ \(Entity _ Term{..},_) -> + , sortable Nothing (i18nCell MsgTermActive) $ \(Entity _ Term{..},_) -> tickmarkCell termActive - , sortable Nothing "Kurse" $ \(_, E.Value numCourses) -> + , sortable Nothing (i18nCell MsgTermCourseCount) $ \(_, E.Value numCourses) -> cell [whamlet|_{MsgNumCourses numCourses}|] - , sortable (Just "start") "Semesteranfang" $ \(Entity _ Term{..},_) -> + , sortable (Just "start") (i18nCell MsgTermStart) $ \(Entity _ Term{..},_) -> cell $ formatTime SelFormatDate termStart >>= toWidget - , sortable (Just "end") "Semesterende" $ \(Entity _ Term{..},_) -> + , sortable (Just "end") (i18nCell MsgTermEnd) $ \(Entity _ Term{..},_) -> cell $ formatTime SelFormatDate termEnd >>= toWidget - , sortable Nothing "Feiertage im Semester" $ \(Entity _ Term{..},_) -> + , sortable Nothing (i18nCell MsgTermHolidays) $ \(Entity _ Term{..},_) -> cell $ do termHolidays' <- mapM (formatTime SelFormatDate) termHolidays [whamlet| @@ -248,7 +233,7 @@ termToTemplate (Just Term{..}) = TermFormTemplate } newTermForm :: TermFormTemplate -> Form Term -newTermForm template html = do +newTermForm template = validateForm validateTerm $ \html -> do mr <- getMessageRender let tidForm @@ -264,7 +249,7 @@ newTermForm template html = do (fslI MsgTermHolidays & setTooltip MsgMassInputTip) True (tftHolidays template) - (result, widget) <- flip (renderAForm FormStandard) html $ Term + flip (renderAForm FormStandard) html $ Term <$> tidForm <*> areq dayField (fslI MsgTermStartDay & setTooltip MsgTermStartDayTooltip) (tftStart template) <*> areq dayField (fslI MsgTermEndDay & setTooltip MsgTermEndDayTooltip) (tftEnd template) @@ -272,24 +257,3 @@ newTermForm template html = do <*> areq dayField (fslI MsgTermLectureStart) (tftLectureStart template) <*> areq dayField (fslI MsgTermLectureEnd & setTooltip MsgTermLectureEndTooltip) (tftLectureEnd template) <*> areq checkBoxField (fslI MsgTermActive) (tftActive template) - return $ case result of - FormSuccess termResult - | errorMsgs <- validateTerm termResult - , not $ null errorMsgs -> - (FormFailure errorMsgs, - [whamlet| -
-
-

Fehler: -
    - $forall errmsg <- errorMsgs -
  • #{errmsg} - ^{widget} - |] - ) - _ -> (result, widget) -{- - where - set :: Text -> FieldSettings site - set = bfs --} diff --git a/src/Handler/Utils/Form.hs b/src/Handler/Utils/Form.hs index 73bcdb18e..c94045265 100644 --- a/src/Handler/Utils/Form.hs +++ b/src/Handler/Utils/Form.hs @@ -284,21 +284,12 @@ htmlField' = htmlField natFieldI :: (Monad m, Integral i, RenderMessage (HandlerSite m) msg, RenderMessage (HandlerSite m) FormMessage) => msg -> Field m i natFieldI msg = convertField fromInteger toInteger $ checkBool (>= 0) msg $ intMinField 0 -natField :: (Monad m, Integral i, RenderMessage (HandlerSite m) FormMessage) => Text -> Field m i -natField d = convertField fromInteger toInteger $ checkBool (>= 0) (T.append d " muss eine natürliche Zahl sein.") $ intMinField 0 - -natIntField ::(Monad m, RenderMessage (HandlerSite m) FormMessage) => Text -> Field m Integer -natIntField = natField - -posIntField :: (Monad m, Integral i, RenderMessage (HandlerSite m) FormMessage) => Text -> Field m i -posIntField d = convertField fromInteger toInteger $ checkBool (> 0) (T.append d " muss eine positive Zahl sein.") $ intMinField 1 - posIntFieldI :: (Monad m, Integral i, RenderMessage (HandlerSite m) msg, RenderMessage (HandlerSite m) FormMessage) => msg -> Field m i posIntFieldI msg = convertField fromInteger toInteger $ checkBool (> 0) msg $ intMinField 0 -- | Field to request integral number > 'm' -minIntField :: (Monad m, Integral i, Show i, RenderMessage (HandlerSite m) FormMessage) => i -> Text -> Field m i -minIntField m d = checkBool (> m) (T.concat [d," muss größer als ", T.pack $ show m, " sein."]) $ intMinField m +minIntFieldI :: (Monad m, Integral i, Show i, RenderMessage (HandlerSite m) FormMessage, RenderMessage (HandlerSite m) msg) => i -> msg -> Field m i +minIntFieldI m msg = checkBool (> m) msg $ intMinField m pointsField :: (Monad m, HandlerSite m ~ UniWorX) => Field m Points pointsField = pointsFieldMinMax (Just 0) Nothing @@ -831,7 +822,7 @@ sheetGroupAFormReq fs template = multiActionA selOptions fs (classify' <$> templ where selOptions = Map.fromList [ ( Arbitrary', Arbitrary - <$> apreq (natField "Gruppengröße") (fslI MsgSheetGroupMaxGroupsize & noValidate) (preview _maxParticipants =<< template) + <$> apreq (natFieldI MsgGroupSizeNotNatural) (fslI MsgSheetGroupMaxGroupsize & noValidate) (preview _maxParticipants =<< template) ) , ( RegisteredGroups', pure RegisteredGroups ) , ( NoGroups', pure NoGroups ) @@ -861,6 +852,10 @@ dayTimeField fs mutc = do | otherwise = (Nothing,Nothing) -} +fieldTimeFormat :: String +-- fieldTimeFormat = "%e.%m.%y %k:%M" +fieldTimeFormat = "%Y-%m-%dT%H:%M:%S" + localTimeField :: (MonadHandler m, HandlerSite m ~ UniWorX) => Field m LocalTime localTimeField = Field { fieldParse = parseHelperGen readTime @@ -873,11 +868,7 @@ localTimeField = Field , fieldEnctype = UrlEncoded } where - fieldTimeFormat :: String - --fieldTimeFormat = "%e.%m.%y %k:%M" - fieldTimeFormat = "%Y-%m-%dT%H:%M:%S" - - -- `defaultTimeLocale` is okay here, since `fieldTimeFormat` does not contain any + -- `defaultTimeLocale` is okay here, since `fieldTimeFormat` does not contain any words readTime :: Text -> Either UniWorXMessage LocalTime readTime t = case parseTimeM True defaultTimeLocale fieldTimeFormat (T.unpack t) of @@ -1149,7 +1140,7 @@ multiUserField onlySuggested suggestions = Field{..} case dbRes of [] -> return $ Left email [E.Value uid] -> return $ Right uid - _other -> throwE $ SomeMessage ("Ambiguous e-mail addr" :: Text) + _other -> throwE $ SomeMessage MsgAmbiguousEmail examResultField :: forall m res. ( MonadHandler m diff --git a/src/Handler/Utils/I18n.hs b/src/Handler/Utils/I18n.hs index 1119191d2..f3b2e157a 100644 --- a/src/Handler/Utils/I18n.hs +++ b/src/Handler/Utils/I18n.hs @@ -9,6 +9,11 @@ import Language.Haskell.TH.Syntax (qRunIO) import qualified Data.List as List import qualified Data.List.NonEmpty as NonEmpty +import qualified Data.Set as Set +import qualified Data.Map as Map + +import qualified Data.Text as Text + import System.Directory (listDirectory) import System.FilePath.Posix (takeBaseName) @@ -40,4 +45,24 @@ i18nWidgetFile basename = do [ funD ws $ [ clause [litP $ stringL l] (normalB . widgetFile $ "i18n" basename l) [] | l <- unpack <$> NonEmpty.toList availableTranslations' -- One function definition for every available language ] ++ [ clause [wildP] (normalB [e| error "selectLanguage returned an invalid translation" |]) [] ] -- Fallback mostly there so compiler does not complain about non-exhaustive pattern match - ] [e|selectLanguage availableTranslations' >>= $(varE ws)|] \ No newline at end of file + ] [e|selectLanguage availableTranslations' >>= $(varE ws)|] + +i18nWidgetFiles :: FilePath -> Q Exp +i18nWidgetFiles basename = do + let i18nDirectory = "templates" "i18n" basename + availableFiles <- qRunIO $ listDirectory i18nDirectory + let fileKinds' = fmap (pack . dropExtension . takeBaseName &&& toTranslation . pack . takeBaseName) availableFiles + fileKinds :: Map Text [Text] + fileKinds = sortWith (NTop . flip List.elemIndex (NonEmpty.toList appLanguages)) . Set.toList <$> Map.fromListWith Set.union [ (kind, Set.singleton l) | (kind, Just l) <- fileKinds' ] + toTranslation fName = listToMaybe . sortOn length . mapMaybe (flip Text.stripPrefix fName) $ map fst fileKinds' + + availableTranslations' <- iforM fileKinds $ \kind -> maybe (fail $ "‘" <> i18nDirectory <> "’ has no translations for ‘" <> unpack kind <> "’") return . NonEmpty.nonEmpty + + -- Dispatch to correct language (depending on user settings via `selectLanguage`) at run time + ws <- newName "ws" -- Name for dispatch function + letE + [ funD ws $ [ clause [litP $ stringL kind, litP $ stringL l] (normalB . widgetFile $ "i18n" basename kind <.> l) [] + | (unpack -> kind, ls) <- Map.toList availableTranslations' + , l <- unpack <$> NonEmpty.toList ls + ] ++ [ clause [wildP, wildP] (normalB [e| error "selectLanguage returned an invalid translation" |]) [] ] -- Fallback mostly there so compiler does not complain about non-exhaustive pattern match + ] [e|imap (\kind ls -> selectLanguage ls >>= $(varE ws) kind) availableTranslations'|] diff --git a/templates/glossary.cassius b/templates/glossary.cassius new file mode 100644 index 000000000..226288fc5 --- /dev/null +++ b/templates/glossary.cassius @@ -0,0 +1,14 @@ +.glossary + dt, .dt + font-weight: 600 + + &.sec + font-style: italic + font-size: 0.9rem + font-weight: 600 + color: var(--color-fontsec) + dd, .dd + margin-left: 12px + + dd + dt, .dd + dt, dd + .dt, .dd + .dt + margin-top: 17px \ No newline at end of file diff --git a/templates/glossary.hamlet b/templates/glossary.hamlet new file mode 100644 index 000000000..5feaa5dfa --- /dev/null +++ b/templates/glossary.hamlet @@ -0,0 +1,5 @@ +$newline never +
    + $forall (term, rTerm, wgt) <- entries' +
    #{rTerm} + ^{wgt} diff --git a/templates/i18n/glossary/applicant.de-de-formal.hamlet b/templates/i18n/glossary/applicant.de-de-formal.hamlet new file mode 100644 index 000000000..700dac30a --- /dev/null +++ b/templates/i18n/glossary/applicant.de-de-formal.hamlet @@ -0,0 +1,8 @@ +$newline never +
    + Bewerbung +
    + Zu einem Kurs bewerben +
    + Studierende können eine Bewerbung für einen Kurs hinterlegen.
    + Wird die Bewerbung akzeptiert (oder der Studierende dem Kurs zugeteilt) wird der Studierende zusätzlich Kursteilnehmer. diff --git a/templates/i18n/glossary/course-participant.de-de-formal.hamlet b/templates/i18n/glossary/course-participant.de-de-formal.hamlet new file mode 100644 index 000000000..5c543e376 --- /dev/null +++ b/templates/i18n/glossary/course-participant.de-de-formal.hamlet @@ -0,0 +1,8 @@ +$newline never +
    + Zu einem Kurs anmelden +
    + Von einem Kurs abmelden +
    + Studierende, die sich explizit mit einem Kurs assoziiert haben (oder ihm zugeteilt wurden).
    + Haben Zugriff auf Material und Teile des Kurses (Anmeldungen zu Tutorien, Prüfungen, ...)