R/datatable.R

Defines functions get_german_translation a11y_renderDataTable

Documented in a11y_renderDataTable

#' Accessible DataTable renderer
#'
#' A wrapper for [DT::renderDataTable()] that enables keyboard navigation
#' (KeyTable extension) by default and provides built-in German/English
#' translations.
#'
#' @param expr Table expression
#' @param lang Language code (`"de"` or `"en"`), or `NULL` if `dt_language` is
#'   set in `expr()`
#' @param dt_language (optional) DT language list (see DT docs); required when
#'   using a language other than `"de"`/`"en"`
#' @param ... Other [DT::renderDataTable()] arguments
#' @return A Shiny render function
#'
#' @examples
#' \donttest{
#' # Inside a Shiny server function
#' if (interactive()) {
#'   library(shiny)
#'   server <- function(input, output, session) {
#'     output$table <- a11y_renderDataTable(
#'       expr = mtcars[, 1:5],
#'       lang = "en"
#'     )
#'   }
#' }
#'
#' # German-language table with Buttons extension and accessible export options
#' if (interactive()) {
#'   library(shiny)
#'   server <- function(input, output, session) {
#'     output$table_de <- a11y_renderDataTable(
#'       expr = head(iris[, 1:4], 10),
#'       lang = "de",
#'       selection = "none",
#'       extensions = c("Buttons"),
#'       options = list(
#'         pageLength = 5,
#'         dom = "Bfrtip",
#'         buttons = c("excel", "csv")
#'       )
#'     )
#'   }
#' }
#' }
#'
#' @importFrom utils modifyList
#' @export
a11y_renderDataTable <- function(expr,
                                 lang = NULL,
                                 dt_language = NULL,
                                 ...) {
  args <- list(...)
  # Extract and remove options from ... to prevent duplication in DT::datatable
  options_list <- if (!is.null(args$options)) args$options else list()
  args$options <- NULL

  # Active KeyTable per Default (for enhanced keyboard usage)
  extensions_list <- if (!is.null(args$extensions)) args$extensions else character()
  if (!"KeyTable" %in% extensions_list) {
    extensions_list <- unique(c(extensions_list, "KeyTable"))
  }
  args$extensions <- extensions_list
  if (is.null(options_list$keys)) {
    options_list$keys <- TRUE
  }

  # Check for copy, pdf, and print in buttons-options
  all_extensions <- unique(c("Buttons", unlist(args$extensions, use.names = FALSE)))
  has_inaccessible_button <- FALSE
  inaccessible_buttons <- c()
  for (btn in c("copy", "pdf", "print")) {
    if (!is.null(options_list$buttons) && (btn %in% options_list$buttons || any(grepl(btn, options_list$buttons)))) {
      has_inaccessible_button <- TRUE
      inaccessible_buttons <- c(inaccessible_buttons, btn)
    }
  }
  # Warning if any of these buttons is used
  if ("Buttons" %in% all_extensions && has_inaccessible_button) {
    warning(
      "a11y_renderDataTable: The Copy, Pdf and Print DataTable (Buttons extension) buttons are not accessible; ",
      "the modal dialog and the opening of tabs is difficult for screenreader and keyboard users. ",
      "Consider disabling these buttons or offering accessible alternatives",
      call. = FALSE
    )
  }
  # Warning if filter is used
  filter_is_set <- (!is.null(args$filter)) && (tolower(args$filter) %in% c("top", "bottom"))
  if (filter_is_set) {
    warning(
      "a11y_renderDataTable: DataTable column filters (filter = \"top\"/\"bottom\") are not accessible by default; ",
      "especially the numeric range filter is not screenreader and keyboard accessible. ",
      "Consider documenting this or using accessible alternatives",
      call. = FALSE
    )
  }

  # dt_language is passed to DT::datatable during the render process
  DT::renderDataTable({
    table <- eval(expr)
    # Language object may be passed if needed (CDN)
    if (!is.null(dt_language)) {
      do.call(DT::datatable, c(list(table,
        language = dt_language,
        options = options_list
      ), args))
    } else if (!is.null(lang) && lang == "de") {
      options_list$language$url <- NULL
      do.call(
        DT::datatable,
        c(list(table,
          options = modifyList(
            list(
              language =
                get_german_translation()
            ),
            options_list
          )
        ), args)
      )
    } else if (!is.null(lang) && lang == "en") {
      do.call(DT::datatable, c(list(table, options = options_list), args))
    } else {
      # Internationalisation is only set for de/en by default, other languages must be set via plug-in explicitly
      stop("a11y_renderDataTable: Please set 'lang' (de/en) or 'dt_language' (see DT::datatable documentation for details) explicitly", call. = FALSE)
    }
  })
}

#' German translations for DataTables
#'
#' Returns a list with German language strings that can be used with the
#' **DataTables** jQuery plugin (e.g., via the `language` option of the
#' `datatable()` function from the **DT** package).
#'
#' @return A nested list containing all translation strings required by
#'   DataTables. Sub-lists are provided for `oPaginate`, `oAria`,
#'   `select$rows`, and `buttons$copySuccess` to match the JSON structure
#'   expected by DataTables.
#'
#' @noRd
get_german_translation <- function() {
  list(
    sEmptyTable = "Keine Daten in der Tabelle vorhanden",
    sInfo = "_START_ bis _END_ von _TOTAL_ Eintr\u00e4gen",
    sInfoEmpty = "Keine Daten vorhanden",
    sInfoFiltered = "(gefiltert von _MAX_ Eintr\u00e4gen)",
    sInfoPostFix = "",
    sInfoThousands = ".",
    sLengthMenu = "_MENU_ Eintr\u00e4ge anzeigen",
    sLoadingRecords = "Wird geladen ..",
    sProcessing = "Bitte warten ..",
    sSearch = "Suchen",
    sZeroRecords = "Keine Eintr\u00e4ge vorhanden",
    oPaginate = list(
      sFirst    = "Erste",
      sPrevious = "Zur\u00fcck",
      sNext     = "N\u00e4chste",
      sLast     = "Letzte"
    ),
    oAria = list(
      sSortAscending  = ": aktivieren, um Spalte aufsteigend zu sortieren",
      sSortDescending = ": aktivieren, um Spalte absteigend zu sortieren"
    ),
    select = list(
      rows = list(
        `0`       = "",
        `1`       = "1 Zeile ausgew\u00e4hlt",
        `_`       = "%d Zeilen ausgew\u00e4hlt"
      )
    ),
    buttons = list(
      print = "Drucken",
      colvis = "Spalten",
      copy = "Kopieren",
      copyTitle = "In Zwischenablage kopieren",
      copyKeys = "Taste <i>ctrl</i> oder <i>\u2318</i> + <i>C</i> um Tabelle<br>in Zwischenspeicher zu kopieren.<br><br>Um abzubrechen die Nachricht anklicken oder Escape dr\u00fccken.",
      copySuccess = list(
        `_` = "%d Spalten kopiert",
        `1` = "1 Spalte kopiert"
      )
    )
  )
}

Try the a11yShiny package in your browser

Any scripts or data that you put into this service are public.

a11yShiny documentation built on April 1, 2026, 5:07 p.m.