diff --git a/messages/uniworx/de.msg b/messages/uniworx/de.msg index c4862d134..7a0d62459 100644 --- a/messages/uniworx/de.msg +++ b/messages/uniworx/de.msg @@ -181,7 +181,7 @@ CourseApplicationRated: Bewertung erfolgreich angepasst CourseApplicationRatingDeleted: Bewertung erfolgreich entfernt CourseApplicationDeleted csh@CourseShorthand: Bewerbung zu #{csh} erfolgreich zurückgezogen -CourseApplicationTitle displayName@Text csh@CourseShorthand: Bewerbung für #{csh}: #{displayName} +CourseApplicationTitle displayName@Text csh@CourseShorthand: Bewerbung für #{csh}: #{displayName} CourseApplicationText: Text-Bewerbung CourseApplicationFollowInstructions: Beachten Sie die Anweisungen zur Bewerbung! @@ -190,7 +190,7 @@ CourseRegistrationFollowInstructions: Beachten Sie die Anweisungen zur Anmeldung CourseApplicationFile: Bewerbung CourseApplicationFiles: Bewerbungsdatei(en) -CourseApplicationArchive: Zip-Archiv der Bewerbungsdatei(en) +CourseApplicationArchive: Zip-Archiv der Bewerbungsdatei(en) CourseRegistrationFile: Datei zur Anmeldung CourseRegistrationFiles: Datei(en) zur Anmeldung CourseRegistrationArchive: Zip-Archiv der Datei(en) zur Anmeldung @@ -1378,6 +1378,7 @@ CsvImportConfirmationTip: Durch den CSV-Import würden die unten aufgeführten CsvImportUnnecessary: Durch den CSV-Import würden keine Änderungen vorgenommen werden CsvImportSuccessful n@Int: CSV-Import erfolgreich, es #{pluralDE n "wurde eine Aktion" (mappend (mappend "wurden " (toMessage n)) " Aktionen")} durchgeführt CsvImportAborted: CSV-Import abgebrochen +CsvImportExplanationLabel: Hinweise zum CSV-Import Proportion c@Text of@Text prop@Rational: #{c}/#{of} (#{rationalToFixed2 (100 * prop)}%) diff --git a/src/Handler/Utils.hs b/src/Handler/Utils.hs index 65e701eed..c0d067554 100644 --- a/src/Handler/Utils.hs +++ b/src/Handler/Utils.hs @@ -15,10 +15,6 @@ import Data.CaseInsensitive (original) -- import qualified Data.CaseInsensitive as CI import qualified Data.Conduit.List as Conduit -import Language.Haskell.TH -import Language.Haskell.TH.Syntax (qRunIO) --- import Language.Haskell.TH.Datatype - import Text.Hamlet (shamletFile) import Handler.Utils.DateTime as Handler.Utils @@ -32,12 +28,9 @@ import Handler.Utils.Rating as Handler.Utils hiding (extractRatings) import Handler.Utils.Sheet as Handler.Utils import Handler.Utils.Mail as Handler.Utils import Handler.Utils.ContentDisposition as Handler.Utils +import Handler.Utils.I18n as Handler.Utils -import System.Directory (listDirectory) -import System.FilePath.Posix (takeBaseName, takeFileName) - -import qualified Data.List as List -import qualified Data.List.NonEmpty as NonEmpty +import System.FilePath.Posix (takeFileName) import Control.Monad.Logger @@ -218,36 +211,6 @@ warnTermDays tid timeNames = do forM_ outoflecture $ warnI MsgDayIsOutOfLecture forM_ outoftermdays $ warnI MsgDayIsOutOfTerm --- | Add language dependent template files --- --- For large files which are translated as a whole. --- --- Argument musst be a directory under @/templates@, --- which contains a file for each language, --- eg. @imprint@ for choosing between --- @/templates/imprint/de.hamlet@, @/templates/imprint/de-at.hamlet@, --- and @/templates/imprint/en.hamlet@ --- --- Dependency detection cannot work properly (no `addDependentFile`-equivalent --- for directories) --- @$ stack clean@ is required so new translations show up -i18nWidgetFile :: FilePath -> Q Exp -i18nWidgetFile basename = do - -- Construct list of available translations (@de@, @en@, ...) at compile time - let i18nDirectory = "templates" "i18n" basename - availableFiles <- qRunIO $ listDirectory i18nDirectory - let availableTranslations = sortWith (NTop . flip List.elemIndex (NonEmpty.toList appLanguages)) . List.nub $ pack . takeBaseName <$> availableFiles - availableTranslations' <- maybe (fail $ "‘" <> i18nDirectory <> "’ is empty") return $ NonEmpty.nonEmpty availableTranslations - - -- 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 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)|] - - -- | return a value only if the current user ist authorized for a given route guardAuthorizedFor :: ( HandlerSite h ~ UniWorX, MonadHandler h, MonadLogger h diff --git a/src/Handler/Utils/I18n.hs b/src/Handler/Utils/I18n.hs new file mode 100644 index 000000000..1119191d2 --- /dev/null +++ b/src/Handler/Utils/I18n.hs @@ -0,0 +1,43 @@ +module Handler.Utils.I18n + where + +import Import + +import Language.Haskell.TH +import Language.Haskell.TH.Syntax (qRunIO) + +import qualified Data.List as List +import qualified Data.List.NonEmpty as NonEmpty + +import System.Directory (listDirectory) +import System.FilePath.Posix (takeBaseName) + + +-- | Add language dependent template files +-- +-- For large files which are translated as a whole. +-- +-- Argument musst be a directory under @/templates@, +-- which contains a file for each language, +-- eg. @imprint@ for choosing between +-- @/templates/imprint/de.hamlet@, @/templates/imprint/de-at.hamlet@, +-- and @/templates/imprint/en.hamlet@ +-- +-- Dependency detection cannot work properly (no `addDependentFile`-equivalent +-- for directories) +-- @$ stack clean@ is required so new translations show up +i18nWidgetFile :: FilePath -> Q Exp +i18nWidgetFile basename = do + -- Construct list of available translations (@de@, @en@, ...) at compile time + let i18nDirectory = "templates" "i18n" basename + availableFiles <- qRunIO $ listDirectory i18nDirectory + let availableTranslations = sortWith (NTop . flip List.elemIndex (NonEmpty.toList appLanguages)) . List.nub $ pack . takeBaseName <$> availableFiles + availableTranslations' <- maybe (fail $ "‘" <> i18nDirectory <> "’ is empty") return $ NonEmpty.nonEmpty availableTranslations + + -- 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 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 diff --git a/src/Handler/Utils/Table/Pagination.hs b/src/Handler/Utils/Table/Pagination.hs index e4560d8eb..ff5e907f3 100644 --- a/src/Handler/Utils/Table/Pagination.hs +++ b/src/Handler/Utils/Table/Pagination.hs @@ -42,6 +42,7 @@ import Handler.Utils.Table.Pagination.CsvColumnExplanations import Handler.Utils.Form import Handler.Utils.Csv import Handler.Utils.ContentDisposition +import Handler.Utils.I18n import Utils import Utils.Lens @@ -281,7 +282,7 @@ piIsUnset PaginationInput{..} = and , isNothing piPage ] - + data DBCsvActionMode = DBCsvActionNew | DBCsvActionExisting | DBCsvActionMissing deriving (Read, Show, Eq, Ord, Enum, Bounded, Generic, Typeable) instance Universe DBCsvActionMode @@ -851,6 +852,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db , formSubmit = FormSubmit , formAnchor = Nothing :: Maybe Text } + csvImportExplanation = modal [whamlet|_{MsgCsvImportExplanationLabel}|] $ Right $(i18nWidgetFile "table/csv-import-explanation") csvColExplanations = case dbtCsvEncode of (Just (Dict, _) :: DBTCsvEncode _ csv) -> assertM' (not . null) . Map.toList . csvColumnsExplanations $ Proxy @csv Nothing -> Nothing @@ -921,7 +923,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db | otherwise -> return $ DBCsvDiffNew rowKey row mapM_ fileSourceCsv dbCsvFiles .| C.mapM toDiff - + seen <- State.get forM_ (Map.toList existing) $ \(rowKey, oldRow) -> if | Map.member rowKey seen -> return () @@ -938,7 +940,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db -> let doHandle | Just inpCsv <- x ^? _dbCsvNew = handle $ throwM . (DBCsvException (toNamedRecord inpCsv) :: Text -> DBCsvException k') <=< dbtCsvRenderException - | otherwise + | otherwise = id in C.sourceList <=< lift . doHandle . runConduit $ dbtCsvComputeActions x .| C.foldMap pure innerAct .| C.fold accActionMap Map.empty @@ -954,7 +956,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db let precomputeIdents :: forall f m'. (Eq (Element f), MonoFoldable f, MonadHandler m') => f -> m' (Element f -> Text) - precomputeIdents = foldM (\f act -> (\id' x -> bool (f x) id' $ act == x) <$> newIdent) (\_ -> error "No id precomputed") + precomputeIdents = foldM (\f act -> (\id' x -> bool (f x) id' $ act == x) <$> newIdent) (\_ -> error "No id precomputed") actionClassIdent <- precomputeIdents $ Map.keys actionMap actionIdent <- precomputeIdents . Set.unions $ Map.elems actionMap @@ -980,7 +982,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db , formSubmit = FormSubmit , formAnchor = Nothing :: Maybe Text } - + $(widgetFile "csv-import-confirmation-wrapper") let defaultHeaderOrder = headerOrder (error "not to be forced" :: csv) diff --git a/templates/i18n/table/csv-import-explanation/de.hamlet b/templates/i18n/table/csv-import-explanation/de.hamlet new file mode 100644 index 000000000..0baade290 --- /dev/null +++ b/templates/i18n/table/csv-import-explanation/de.hamlet @@ -0,0 +1,21 @@ +

Hinweise zum Import von CSV-Dateien +
+
Änderungen +
+ Alle Werte können durch den Import verändert werden, + auch bereits Beim Export bereits vorhandene Werte. +
Vorschau +
+ Bevor Werte geändert werden, wird eine Vorschau der Änderungen angezeigt. +
Leere Zellen +
+ Löschbare Zellen werden durch leere Zellen gelöscht oder auf eindeutige Werte gesetzt. +
Konsistenz +
+

+ Es werden nur konsistente Änderungen akzeptiert! +

+ Daraus folgt, dass es sinnvoll sein kann, gewisse Zellen frei zu lassen; + z.B. ändert man ein Studienfachzuordnung eines Teilnehmers ab, + dann müsste man auch Abschluss und Semesterzahl passend ändern. + Da diese jedoch eindeutig sind, kann man diese Zellen einfach frei lassen. diff --git a/templates/table/csv-transcode.hamlet b/templates/table/csv-transcode.hamlet index 11586a227..00f76287b 100644 --- a/templates/table/csv-transcode.hamlet +++ b/templates/table/csv-transcode.hamlet @@ -4,6 +4,7 @@ $if is _Just dbtCsvDecode

_{MsgTableHeadingCsvImport}
+ ^{csvImportExplanation} ^{csvImportWdgt'} $if is _Just dbtCsvEncode
diff --git a/templates/table/csv-transcode.lucius b/templates/table/csv-transcode.lucius index 7c6de1c2a..dbf6b124e 100644 --- a/templates/table/csv-transcode.lucius +++ b/templates/table/csv-transcode.lucius @@ -1,6 +1,6 @@ .csv-export { margin-bottom: 13px; - + .csv-export__content { display: flex; align-content: space-between;