diff --git a/.lintr b/.lintr new file mode 100644 index 00000000..78c59f0f --- /dev/null +++ b/.lintr @@ -0,0 +1,36 @@ +linters: linters_with_defaults( + fixed_regex_linter(), + function_argument_linter(), + implicit_integer_linter(allow_colon = TRUE), + infix_spaces_linter(exclude_operators = c("=", "*", "/")), + # TODO(michaelchirico): Enable once quotes used for parallelism are supported + # keyword_quote_linter(), + line_length_linter(120L), + string_boundary_linter(), + undesirable_function_linter(c( + sapply = NA + )), + # TODO(michaelchirico): Enable after #2245 + # unnecessary_nested_if_linter(), + assignment_linter = NULL, + # TODO(michaelchirico): reactivate this and spaces_inside_linter() + # once they support 'empty' i argument DT[ , j] + commas_linter = NULL, + commented_code_linter = NULL, + cyclocomp_linter = NULL, + # TODO(michaelchirico): reactivate this. far too many + # false positives for now. + indentation_linter = NULL, + object_name_linter = NULL, + quotes_linter = NULL, # TODO(michaelchirico): switch to "'", + spaces_inside_linter = NULL + ) +exclusions: list( + "inst/pkg", + "tests/testthat" = list(object_usage_linter = Inf), + "tests/testthat/test_packages", + "vignettes" = list( + implicit_integer_linter = Inf, + undesirable_function_linter = Inf + ) + ) diff --git a/R/check_untranslated_cat.R b/R/check_untranslated_cat.R index e802fdc7..d3f49604 100644 --- a/R/check_untranslated_cat.R +++ b/R/check_untranslated_cat.R @@ -52,7 +52,7 @@ #' unlink(tmp_pkg, recursive = TRUE) #' rm(pkg, tmp_pkg, message_data) #' @export -check_untranslated_cat <- function (message_data) { +check_untranslated_cat <- function(message_data) { if (!is.data.table(message_data)) message_data = as.data.table(message_data) # not iron-clad but it's a good first pass cat_calls = unique( diff --git a/R/check_untranslated_src.R b/R/check_untranslated_src.R index 411e0ef9..7da93899 100644 --- a/R/check_untranslated_src.R +++ b/R/check_untranslated_src.R @@ -41,7 +41,7 @@ #' unlink(tmp_pkg, recursive = TRUE) #' rm(pkg, tmp_pkg, message_data) #' @export -check_untranslated_src <- function (message_data) { +check_untranslated_src <- function(message_data) { if (!is.data.table(message_data)) message_data = as.data.table(message_data) return(message_data[ diff --git a/R/explain_plurals.R b/R/explain_plurals.R index ae34a0fd..c6248bed 100644 --- a/R/explain_plurals.R +++ b/R/explain_plurals.R @@ -15,7 +15,7 @@ po_explain_plurals <- function(language, index) { "Supply one language code (see ?translate_package)" = is.character(language) && length(language) == 1L, "If supplied, `index` should be a single non-negative number" = - missing(index) || (is.numeric(index) && length(index) == 1L && index > 0) + missing(index) || (is.numeric(index) && length(index) == 1L && index > 0L) ) language_metadata <- .potools$KNOWN_LANGUAGES[.(language), nomatch = NULL] if (!nrow(language_metadata)) { diff --git a/R/get_po_messages.R b/R/get_po_messages.R index c8f4b2a6..b59041d1 100644 --- a/R/get_po_messages.R +++ b/R/get_po_messages.R @@ -47,7 +47,7 @@ get_po_messages <- function(po_file) { stopf("Found %d msgid which differs from %d msgstr; corrupted .po file", n_singular, length(msgstr_start)) } - if ((n_plural == 0L && n_msgstr_plural > 0L) || (n_plural > 0 && n_msgstr_plural %% n_plural != 0L)) { + if ((n_plural == 0L && n_msgstr_plural > 0L) || (n_plural > 0L && n_msgstr_plural %% n_plural != 0L)) { stopf( "Found %d msgid_plural, which does not evenly divide %d msgstr[n]; corrupted .po file", n_msgstr_plural, n_plural @@ -55,7 +55,7 @@ get_po_messages <- function(po_file) { } # pre-calculate which lines contain message continuations. Append # FALSE for a while loop to terminate gracefully on hitting file end - is_msg_continuation = c(grepl('^"', po_lines), FALSE) + is_msg_continuation = c(startsWith(po_lines, '"'), FALSE) po_data = data.table( message_source = message_source, @@ -88,7 +88,7 @@ get_po_messages <- function(po_file) { end = find_msg_end(start) set(po_data, msg_j, 'msgid', build_msg(start, end, 'msgid')) - set(po_data, msg_j, 'fuzzy', as.integer(start != 1L && grepl("^#, fuzzy", po_lines[start-1L]))) + set(po_data, msg_j, 'fuzzy', as.integer(start != 1L && startsWith(po_lines[start - 1L], "#, fuzzy"))) start = end + 1L end = find_msg_end(start) @@ -102,7 +102,7 @@ get_po_messages <- function(po_file) { end = find_msg_end(start) msg1 = build_msg(start, end, 'msgid') - set(po_data, msg_j, 'fuzzy', as.integer(start != 1L && grepl("^#, fuzzy", po_lines[start-1L]))) + set(po_data, msg_j, 'fuzzy', as.integer(start != 1L && startsWith(po_lines[start - 1L], "#, fuzzy"))) start = end + 1L end = find_msg_end(start) @@ -112,7 +112,7 @@ get_po_messages <- function(po_file) { start = end + 1L msgstr_plural = character() - while (start <= po_length && grepl('^msgstr\\[', po_lines[start])) { + while (start <= po_length && startsWith(po_lines[start], 'msgstr[')) { end = find_msg_end(start) msgstr_plural = c(msgstr_plural, build_msg(start, end, 'msgstr\\[\\d+\\]')) start = end + 1L diff --git a/R/get_r_messages.R b/R/get_r_messages.R index e5035c8b..897a4cd5 100644 --- a/R/get_r_messages.R +++ b/R/get_r_messages.R @@ -1,6 +1,6 @@ # Spiritual cousin version of tools::{x,xn}gettext. Instead of iterating the AST # as R objects, do so from the parse data given by utils::getParseData(). -get_r_messages <- function (dir, custom_translation_functions = NULL, is_base = FALSE, style = c("base", "explicit")) { +get_r_messages <- function(dir, custom_translation_functions = NULL, is_base = FALSE, style = c("base", "explicit")) { style <- match.arg(style) expr_data <- rbindlist(lapply(parse_r_files(dir, is_base), getParseData), idcol = 'file') @@ -104,7 +104,7 @@ get_r_messages <- function (dir, custom_translation_functions = NULL, is_base = msg_files = unique(msg$file) if (is_base) { paths <- file.path(dir, 'R', msg_files) - share_idx <- grepl('^share/R', msg_files) + share_idx <- startsWith(msg_files, 'share/R') paths[share_idx] <- file.path(dir, '../../..', msg_files[share_idx]) } else { paths <- file.path(dir, 'R', msg_files) @@ -183,6 +183,7 @@ parse_r_files = function(dir, is_base) { if (!dir.exists(file.path(r_share_dir, 'R'))) { # templated to share with src-side message stopf( + # nolint next: line_length_linter. "Translation of the 'base' package can only be done on a local mirror of r-devel. Such a copy has a file %s at the top level that is required to proceed.", "share/R/REMOVE.R" ) @@ -208,6 +209,7 @@ parse_r_keywords = function(spec) { if (ncol(keyval) != 2L) { idx <- if (ncol(keyval) == 1L) seq_along(spec) else which(is.na(keyval$V2)) stopf( + # nolint next: line_length_linter. "Invalid custom translator specification(s): %s.\nAll inputs for R must be key-value pairs like fn:arg1|n1[,arg2|n2] or fn:...\\arg1,...,argn.", toString(spec[idx]) ) @@ -219,6 +221,7 @@ parse_r_keywords = function(spec) { dots_idx = grepl("^[.]{3}[\\](?:[a-zA-Z0-9._]+,)*[a-zA-Z0-9._]+$", keyval$V2) if (any(idx <- !named_idx & !dots_idx & !plural_idx)) { stopf( + # nolint next: line_length_linter. "Invalid custom translator specification(s): %s.\nAll inputs for R must be key-value pairs like fn:arg1|n1[,arg2|n2] or fn:...\\arg1,...,argn.", toString(spec[idx]) ) @@ -228,7 +231,7 @@ parse_r_keywords = function(spec) { singular = list( dots = lapply( which(dots_idx), - function(ii) list( + function(ii) list( # nolint: brace_linter. fname = keyval$V1[ii], excluded_args = strsplit(gsub("^[.]{3}[\\]", "", keyval$V2[ii]), ",", fixed = TRUE)[[1L]] ) @@ -305,7 +308,9 @@ domain_fmt_funs <- function(use_conditions = TRUE) { NON_DOTS_ARGS = c("domain", "call.", "appendLF", "immediate.", "noBreaks.") # for functions (e.g. domain_dots_funs) where we extract strings from ... arguments -get_dots_strings = function(expr_data, funs, arg_names, exclude = c('gettext', 'gettextf', 'ngettext'), recursive = TRUE) { +get_dots_strings = function(expr_data, funs, arg_names, + exclude = c('gettext', 'gettextf', 'ngettext'), + recursive = TRUE) { call_neighbors = get_call_args(expr_data, funs) call_neighbors = drop_suppressed_and_named(call_neighbors, expr_data, arg_names) @@ -344,8 +349,9 @@ get_named_arg_strings = function(expr_data, fun, args, recursive = FALSE, plural idx = shift(token, fill = '') == 'SYMBOL_SUB' & shift(text, fill = '') %chin% names(args) if (any(idx) & !all(matched <- names(args) %chin% text[token == 'SYMBOL_SUB'])) { stopf( + # nolint next: line_length_linter. "In line %s of %s, found a call to %s that names only some of its messaging arguments explicitly. Expected all of [%s] to be named. Please name all or none of these arguments.", - expr_data[.BY, on = c(id = 'ancestor'), line1[1L]], .BY$file, .BY$fname, toString(names(args)) + expr_data[.BY, on = c(id = 'ancestor'), line1[1L]], .BY$file, .BY$fname, toString(names(args)[!matched]) ) } .(id = id[idx]) @@ -486,8 +492,8 @@ build_call = function(lines, comments, params) { } adjust_tabs = function(l) { - while((idx <- regexpr("\t", l, fixed = TRUE)) > 0L) { - l = sub("\t", strrep(" ", 9L-(idx %% 8L)), l, fixed = TRUE) + while ((idx <- regexpr("\t", l, fixed = TRUE)) > 0L) { + l = sub("\t", strrep(" ", 9L - (idx %% 8L)), l, fixed = TRUE) } l } @@ -523,26 +529,30 @@ clean_text = function(x) { return(x) } -string_schema = function() data.table( - file = character(), - # needed to build the call - parent = integer(), - # needed to order the strings correctly within the call - id = integer(), - fname = character(), - msgid = character(), - msgid_plural = list() -) +string_schema = function() { + data.table( + file = character(), + # needed to build the call + parent = integer(), + # needed to order the strings correctly within the call + id = integer(), + fname = character(), + msgid = character(), + msgid_plural = list() + ) +} # the schema for empty edge cases -r_message_schema = function() data.table( - type = character(), - file = character(), - msgid = character(), - msgid_plural = list(), - line_number = integer(), - call = character(), - is_repeat = logical(), - is_marked_for_translation = logical(), - is_templated = logical() -) +r_message_schema = function() { + data.table( + type = character(), + file = character(), + msgid = character(), + msgid_plural = list(), + line_number = integer(), + call = character(), + is_repeat = logical(), + is_marked_for_translation = logical(), + is_templated = logical() + ) +} diff --git a/R/get_src_messages.R b/R/get_src_messages.R index c2521684..7dddda3a 100644 --- a/R/get_src_messages.R +++ b/R/get_src_messages.R @@ -8,6 +8,7 @@ get_src_messages = function( if (!file.exists(potfiles_loc)) { # templated to share with R-side message stopf( + # nolint next: line_length_linter. "Translation of the 'base' package can only be done on a local mirror of r-devel. Such a copy has a file %s at the top level that is required to proceed.", "po/POTFILES" ) @@ -55,10 +56,10 @@ get_src_messages = function( # all calls to certain functions are marked as templated, regardless of template markers, #137 msg[ , "is_templated" := - fname %chin% TEMPLATE_CALLS - | grepl(SPRINTF_TEMPLATE_REGEX, msgid, perl=TRUE) - | vapply(msgid_plural, function(str) any(grepl(SPRINTF_TEMPLATE_REGEX, str, perl=TRUE)), logical(1L)) - ] + fname %chin% TEMPLATE_CALLS + | grepl(SPRINTF_TEMPLATE_REGEX, msgid, perl=TRUE) + | vapply(msgid_plural, function(str) any(grepl(SPRINTF_TEMPLATE_REGEX, str, perl=TRUE)), logical(1L)) + ] msg[ , "fname" := NULL] # TODO: write this @@ -266,6 +267,7 @@ parse_src_keywords = function(spec) { if (!all(idx <- grepl("[a-zA-Z0-9_]+:[0-9]+", spec))) { stopf( + # nolint next: line_length_linter. "Invalid custom translator specification(s): %s.\nAll inputs for src must be key-value pairs like fn:arg1. Custom plural messagers are not yet supported.", toString(spec[!idx]) ) @@ -290,27 +292,31 @@ preprocess = function(contents) { '"' = { ii = ii + 1L while (ii < nn - 1L) { + # nolint start: brace_linter. switch( contents[ii], '"' = break, "\\" = { ii = ii + 2L }, { ii = ii + 1L } ) + # nolint end: brace_linter. } }, # " as a char ('"') also presents an issue for char array detection - "'" = { ii = ii + 2L + (contents[ii+1L] == '\\')}, + "'" = { + ii = ii + 2L + (contents[ii + 1L] == '\\') + }, "/" = { jj = 0L if (contents[ii + 1L] == "/") { jj = ii + 2L - while (jj <= nn && contents[jj] != "\n") { jj = jj + 1L } + while (jj <= nn && contents[jj] != "\n") jj = jj + 1L # blank the comment, not the newline; also don't overwrite \r on Windows - contents[ii:(jj - 1L - (contents[jj-1L] == "\r"))] = " " + contents[ii:(jj - 1L - (contents[jj - 1L] == "\r"))] = " " ii = jj } else if (contents[ii + 1L] == "*") { jj = ii + 2L - while (jj <= nn - 1L && (contents[jj] != "*" || contents[jj + 1L] != "/")) { jj = jj + 1L } + while (jj <= nn - 1L && (contents[jj] != "*" || contents[jj + 1L] != "/")) jj = jj + 1L idx = ii:(jj + 1L) # <3 windows contents[idx] = fifelse(contents[idx] %chin% c("\n", "\r"), contents[idx], " ") @@ -321,18 +327,18 @@ preprocess = function(contents) { # we ignore some possibilities involving LEAST/FAST/MAX/PTR and only allow 32/64 widths, for simplicity. "P" = { if ( - ii < nn-6L - && (ii == 1L || !contents[ii-1L] %chin% C_IDENTIFIER_CHARS) - && contents[ii+1L] == "R" && contents[ii+2L] == "I" - && contents[ii+3L] %chin% c("d", "i", "o", "u", "x", "X") + ii < nn - 6L + && (ii == 1L || !contents[ii - 1L] %chin% C_IDENTIFIER_CHARS) + && contents[ii + 1L] == "R" && contents[ii + 2L] == "I" + && contents[ii + 3L] %chin% c("d", "i", "o", "u", "x", "X") && ( - (contents[ii+4L] == "3" && contents[ii+5L] == "2") - || (contents[ii+4L] == "6" && contents[ii+5L] == "4") + (contents[ii + 4L] == "3" && contents[ii + 5L] == "2") + || (contents[ii + 4L] == "6" && contents[ii + 5L] == "4") # ensure the identifier ends here, and it's a symbol, not a call - && (ii == nn-7L || !contents[ii+6L] %chin% C_IDENTIFIER_CHARS) + && (ii == nn - 7L || !contents[ii + 6L] %chin% C_IDENTIFIER_CHARS) ) ) { - contents = c(head(contents, ii-1L), '"', '<', contents[ii:(ii+5L)], '>', '"', tail(contents, -ii-5L)) + contents = c(head(contents, ii - 1L), '"', '<', contents[ii:(ii + 5L)], '>', '"', tail(contents, -ii - 5L)) ii = ii + 9L } } @@ -414,7 +420,7 @@ build_msgid = function(left, right, starts, ends, contents) { # Only the first array is extracted from ternary operator usage inside _(), #154 # IINM, ternary operator usage has to come first, i.e., "abc" (test ? "def" : "ghi") won't parse if (endsWith(grout[1L], "?")) { - return(safe_substring(contents, starts[1L]+1L, ends[1L]-1L)) + return(safe_substring(contents, starts[1L] + 1L, ends[1L] - 1L)) } # any unknown macros are not expanded and the recorded array is cut off on encountering one, @@ -425,7 +431,7 @@ build_msgid = function(left, right, starts, ends, contents) { ends = head(ends, keep_idx[1L]) } - paste(safe_substring(contents, starts+1L, ends-1L), collapse = "") + paste(safe_substring(contents, starts + 1L, ends - 1L), collapse = "") } # this could probably go for more stress testing. it didn't _crash_ on base, but @@ -442,7 +448,7 @@ build_msgid_plural = function(fun, left, right, starts, ends, contents, message_ msgid = '' n_grout = length(grout) for (ii in 2:length(grout)) { - msgid = paste0(msgid, safe_substring(contents, starts[ii-1L]+1L, ends[ii-1L]-1L)) + msgid = paste0(msgid, safe_substring(contents, starts[ii - 1L] + 1L, ends[ii - 1L] - 1L)) if (ii == n_grout || grepl(',', grout[ii], fixed = TRUE)) { msgid_plural = c(msgid_plural, msgid) msgid = '' @@ -464,8 +470,8 @@ build_msgid_plural = function(fun, left, right, starts, ends, contents, message_ ii = ii + 1L } jj = ii - while (jj < length(grout) && !grepl(",", grout[jj], fixed = TRUE)) { jj = jj + 1L } - msgid = paste(safe_substring(contents, starts[ii:jj - 1L]+1L, ends[ii:jj - 1L]-1L), collapse = '') + while (jj < length(grout) && !grepl(",", grout[jj], fixed = TRUE)) jj = jj + 1L + msgid = paste(safe_substring(contents, starts[ii:jj - 1L] + 1L, ends[ii:jj - 1L] - 1L), collapse = '') msgid_plural = msgid } return(list(msgid_plural)) @@ -496,7 +502,8 @@ DEFAULT_MESSAGE_CALLS = data.table( "Rprintf", "REprintf", "Rvprintf", "REvprintf", "R_ShowMessage", "R_Suicide", "warning", "Rf_warning", "error", "Rf_error", - "dgettext", # NB: xgettext ignores the domain (first arg) when extracting templates, so we don't bother checking either + # NB: xgettext ignores the domain (first arg) when extracting templates, so we don't bother checking either + "dgettext", "snprintf" ), str_arg = c(0L, rep(1L, 10L), 2L, 3L), @@ -525,23 +532,27 @@ C_IDENTIFIER_REST = "[_a-zA-Z0-9]" # ASCII-sorted C_IDENTIFIER_CHARS = c('(', as.character(0:9), LETTERS, '_', letters) -src_msg_schema = function() data.table( - type = character(), - file = character(), - msgid = character(), - msgid_plural = list(), - line_number = integer(), - call = character(), - is_repeat = logical(), - is_marked_for_translation = logical(), - is_templated = logical() -) +src_msg_schema = function() { + data.table( + type = character(), + file = character(), + msgid = character(), + msgid_plural = list(), + line_number = integer(), + call = character(), + is_repeat = logical(), + is_marked_for_translation = logical(), + is_templated = logical() + ) +} -file_msg_schema = function() data.table( - msgid = character(), - msgid_plural = list(), - line_number = integer(), - fname = character(), - call = character(), - is_marked_for_translation = logical() -) +file_msg_schema = function() { + data.table( + msgid = character(), + msgid_plural = list(), + line_number = integer(), + fname = character(), + call = character(), + is_marked_for_translation = logical() + ) +} diff --git a/R/msgmerge.R b/R/msgmerge.R index 889c8275..df6da6cf 100644 --- a/R/msgmerge.R +++ b/R/msgmerge.R @@ -78,7 +78,7 @@ update_en_quot_mo_files <- function(dir, verbose) { # https://www.gnu.org/software/gettext/manual/html_node/msginit-Invocation.html # https://docs.oracle.com/cd/E36784_01/html/E36870/msginit-1.html#scrolltoc -run_msginit <- function(po_path, pot_path, locale, width = 80, verbose = TRUE) { +run_msginit <- function(po_path, pot_path, locale, width = 80L, verbose = TRUE) { check_potools_sys_reqs("msginit") args <- c( "-i", shQuote(path.expand(pot_path)), @@ -98,4 +98,3 @@ run_msginit <- function(po_path, pot_path, locale, width = 80, verbose = TRUE) { } return(invisible()) } - diff --git a/R/po_create.R b/R/po_create.R index a6f0644a..1d70cb6f 100644 --- a/R/po_create.R +++ b/R/po_create.R @@ -17,7 +17,6 @@ #' @inheritParams po_extract #' @export po_create <- function(languages, dir = ".", verbose = !is_testing()) { - package <- get_desc_data(dir, "Package") po_files <- po_language_files(languages, dir) for (ii in seq_len(nrow(po_files))) { @@ -48,7 +47,7 @@ pot_paths <- function(dir, type, package = NULL) { if (is.null(package)) { package <- get_desc_data(dir, "Package") } - if (length(type) == 0) { + if (length(type) == 0L) { character() } else { file.path(dir, "po", paste0(po_prefix(type), package, ".pot")) diff --git a/R/read_translation.R b/R/read_translation.R index 08fe7462..1ad6b552 100644 --- a/R/read_translation.R +++ b/R/read_translation.R @@ -44,7 +44,7 @@ read_translation = function(msgid, type, file, call, fuzzy, msgstr, metadata) { # add enough blanks for Plural message: paste0("\n ", format(msgid_metadata)), language_color(metadata$full_name_eng), - plural_range_color(.potools$PLURAL_RANGE_STRINGS[.(metadata$plural, jj-1L), range]), + plural_range_color(.potools$PLURAL_RANGE_STRINGS[.(metadata$plural, jj - 1L), range]), fuzzy_tag, domain = "R-potools" )) @@ -93,7 +93,7 @@ unset_prompt_conn <- function() { # (2) readline is _strictly_ interactive -- it can't be tested. # See this post for testing: # https://debruine.github.io/posts/interactive-test/ -prompt = function(..., conn = .potools$prompt_conn, require_type) { +prompt = function(..., conn = .potools$prompt_conn, require_type = NULL) { cat(...) cat('\n') if (inherits(conn, "terminal")) { @@ -108,7 +108,7 @@ prompt = function(..., conn = .potools$prompt_conn, require_type) { # nocov end } # See #105 / #95... confusing stuff - if (missing(require_type)) return(out) + if (is.null(require_type)) return(out) out = type.convert(out, as.is = TRUE) if (typeof(out) == require_type) return(out) diff --git a/R/show_diagnostic_results.R b/R/show_diagnostic_results.R index 1aeaf3fb..92abe65c 100644 --- a/R/show_diagnostic_results.R +++ b/R/show_diagnostic_results.R @@ -12,9 +12,11 @@ show_diagnostic_results <- function(results, diagnostic) { } } -diagnostic_schema = function() data.table( - call = character(), - file = character(), - line_number = integer(), - replacement = character() -) +diagnostic_schema = function() { + data.table( + call = character(), + file = character(), + line_number = integer(), + replacement = character() + ) +} diff --git a/R/specials_metadata.R b/R/specials_metadata.R index 33e9c190..a9cadc77 100644 --- a/R/specials_metadata.R +++ b/R/specials_metadata.R @@ -40,6 +40,7 @@ get_specials_metadata = function(x) { redirect_idx = meta$redirect_start > 0L if (any(template_idx & redirect_idx)) { if (any(template_idx & !redirect_idx)) stopf( + # nolint next: line_length_linter. "Invalid templated message. If any %%N$ redirects are used, all templates must be redirected.\n\tRedirected tempates: %s\n\t Un-redirected templates: %s", meta$special[template_idx & redirect_idx], meta$special[template_idx & !redirect_idx] ) @@ -68,6 +69,7 @@ get_specials_metadata = function(x) { if (any(template_idx & redirect_idx)) { if (nrow(fail <- meta[ , .N, by = c("redirect_id", "id")][ , .N, by = "redirect_id"][N > 1L])) { stopf( + # nolint next: line_length_linter. "Invalid templated message string with redirects -- all messages pointing to the same input must have identical formats, but received %s", meta[ fail, @@ -110,6 +112,7 @@ all.equal.specials_metadata = function(target, current, ...) { if (anyNA(matched$start.x)) { if (isTRUE(all.equal(table(target$id), table(current$id)))) { return(gettextf( + # nolint next: line_length_linter. "received the same set of templates + bordering newlines, but in incorrect order (%s vs %s). Recall that you can use %%$N to do redirect, e.g. to swap the order of '%%d %%s' to be translated more naturally, your translation can use '%%1$s %%2$d'", sprintf("[%s]", toString(target$special)), sprintf("[%s]", toString(current$special)) )) diff --git a/R/translate_package.R b/R/translate_package.R index 089aacfe..d17b000c 100644 --- a/R/translate_package.R +++ b/R/translate_package.R @@ -303,7 +303,7 @@ #' @export translate_package = function( dir = '.', - languages, + languages = NULL, diagnostics = list(check_cracked_messages, check_untranslated_cat, check_untranslated_src), custom_translation_functions = list(R = NULL, src = NULL), max_translations = Inf, @@ -318,7 +318,7 @@ translate_package = function( stopifnot( 'Only one package at a time' = length(dir) == 1L, "'dir' must be a character" = is.character(dir), - "'languages' must be a character vector" = missing(languages) || is.character(languages), + "'languages' must be a character vector" = is.null(languages) || is.character(languages), "'diagnostics' should be empty, a function, or list of functions" = is.null(diagnostics) || is.function(diagnostics) @@ -392,7 +392,7 @@ translate_package = function( } - if (missing(languages)) { + if (is.null(languages)) { if (verbose) message('No languages provided; finishing') return(invisible()) } @@ -473,6 +473,7 @@ translate_package = function( }) if (verbose) { + # nolint start: line_length_linter. message( "***************************\n", "** BEGINNING TRANSLATION **\n", @@ -484,6 +485,7 @@ translate_package = function( " * While the count of templates must match, the _order_ can be changed by using e.g. %2$s to mean 'use the second input as a string here'\n", " * Whenever templates or escaping is happening in a string, these will be 'highlighted' by carets (^) in the line below" ) + # nolint end: line_length_linter. } # NB: loop over rows to facilitate quitting without losing progress for (ii in head(new_idx, max_translations)) { diff --git a/R/utils.R b/R/utils.R index ae2796cc..2e0a58e6 100644 --- a/R/utils.R +++ b/R/utils.R @@ -65,7 +65,7 @@ check_potools_sys_reqs = function(which = SYSTEM_REQUIREMENTS) { RTOOLS_URL ) } else if (Sys.info()['sysname'] == 'Darwin') { - platform_msg = gettext('These GNU tools are commonly available, try installing from brew or apt-get') + platform_msg = gettext('These GNU tools are commonly available, try installing from brew or apt-get') } else { platform_msg = gettext( 'These GNU tools are commonly available from the Linux package manager for your system' @@ -80,7 +80,7 @@ check_potools_sys_reqs = function(which = SYSTEM_REQUIREMENTS) { } # nocov end -list_package_files = function(dir, subdir, subsubdirs = character(), pattern) { +list_package_files = function(dir, subdir, subsubdirs = character(), pattern = NULL) { subdir = file.path(dir, subdir) files = list.files(subdir, pattern = pattern) for (subsubdir in subsubdirs) { @@ -189,4 +189,4 @@ is_outdated <- function(src, dst) { is_testing = function() identical(Sys.getenv("TESTTHAT"), "true") -is_gnu_gettext = function() any(grepl("GNU gettext", system('gettext --version', intern=TRUE))) +is_gnu_gettext = function() any(grepl("GNU gettext", system('gettext --version', intern=TRUE), fixed = TRUE)) diff --git a/R/write_po_file.R b/R/write_po_file.R index a69a9c35..595b730d 100644 --- a/R/write_po_file.R +++ b/R/write_po_file.R @@ -275,8 +275,8 @@ write_po_file <- function( msgid2 = vapply(msgid_plural, `[`, character(1L), 2L) msgid_plural = vapply( msgstr_plural[!singular_idx], - function(msgstr) paste( - sprintf(msgstr_fmt, seq_along(msgstr)-1L, msgstr), + function(msgstr) paste( # nolint: brace_linter. TODO(lintr/2240): remove nolint. + sprintf(msgstr_fmt, seq_along(msgstr) - 1L, msgstr), collapse='\n' ), character(1L) @@ -298,7 +298,7 @@ wrap_msg = function(key, value, width=Inf, wrap_at_newline = TRUE) { # xgettext always wraps at a newline (even if the whole message fits inside 'width') wrap_idx <- nchar(value) + nchar(key) + 3L > width if (wrap_at_newline) { - wrap_idx <- wrap_idx | grepl("[\\]n.", value) + wrap_idx <- wrap_idx | grepl("[\\]n.", value) } out[!wrap_idx] = sprintf('%s "%s"', key, value[!wrap_idx]) out[wrap_idx] = sprintf('%s ""\n%s', key, wrap_strings(value[wrap_idx], width)) @@ -329,7 +329,7 @@ wrap_strings = function(str, width) { sub_str = character(length(newline_indices) - 1L) for (jj in seq_along(sub_str)) { # -2 to exclude \n - sub_str[jj] = substr(str[ii], newline_indices[jj]+1L, newline_indices[jj+1L]-2L) + sub_str[jj] = substr(str[ii], newline_indices[jj] + 1L, newline_indices[jj + 1L] - 2L) } sub_boundaries = gregexpr('[ !,-./:;?|}](?![ !,-./:;?|}])|[^\'](?=\'?%)', sub_str, perl = TRUE) sub_str_widths = nchar(sub_str) diff --git a/tests/testthat/test-po_explain_plurals.R b/tests/testthat/test-po_explain_plurals.R index 2e11ded7..067a1ab9 100644 --- a/tests/testthat/test-po_explain_plurals.R +++ b/tests/testthat/test-po_explain_plurals.R @@ -11,7 +11,11 @@ test_that("po_explain_plurals works as expected", { expect_error(po_explain_plurals(1L), "Supply one language code", fixed=TRUE) expect_error(po_explain_plurals(letters), "Supply one language code", fixed=TRUE) expect_error(po_explain_plurals("en", "a"), "If supplied, `index` should be a single non-negative number", fixed=TRUE) - expect_error(po_explain_plurals("en", 1:10), "If supplied, `index` should be a single non-negative number", fixed=TRUE) + expect_error( + po_explain_plurals("en", 1:10), + "If supplied, `index` should be a single non-negative number", + fixed=TRUE + ) expect_error(po_explain_plurals("en", -1), "If supplied, `index` should be a single non-negative number", fixed=TRUE) expect_error(po_explain_plurals("xx"), "not a known language", fixed=TRUE) diff --git a/tests/testthat/test-translate-package.R b/tests/testthat/test-translate-package.R index 7c5b1fc0..5c3d5eca 100644 --- a/tests/testthat/test-translate-package.R +++ b/tests/testthat/test-translate-package.R @@ -237,7 +237,16 @@ test_that("Packages with src code work correctly", { expect( any(grepl("inst/po/zh_CN/LC_MESSAGES/rSrcMsg.mo", pkg_files, fixed = TRUE)), sprintf( - "Didn't find rSrcMsg.mo; found %s.\n**Sysreq paths: %s.\n**po/zh_CN contents:\n%s\n**Direct msgfmt output:\n%s**Session info:\n%s", + paste( + "Didn't find rSrcMsg.mo; found %s.", + "**Sysreq paths: %s.", + "**po/zh_CN contents:", + "%s", + "**Direct msgfmt output:", + "%s**Session info:", + "%s", + sep = "\n" + ), toString(pkg_files), toString(Sys.which(potools:::SYSTEM_REQUIREMENTS)), paste(readLines(file.path(pkg, 'po/zh_CN.po')), collapse='\n'), { diff --git a/vignettes/GreatSpelling/R/spellman.R b/vignettes/GreatSpelling/R/spellman.R index 1c15f4a3..af228593 100644 --- a/vignettes/GreatSpelling/R/spellman.R +++ b/vignettes/GreatSpelling/R/spellman.R @@ -1,5 +1,4 @@ sorecery = function(x) { if (max(x) > 10) warning("This is byond my abilities!") - log(10-x) + log(10 - x) } - diff --git a/vignettes/custom-diagnostic.Rmd b/vignettes/custom-diagnostic.Rmd index 69be9df3..ac1456f1 100644 --- a/vignettes/custom-diagnostic.Rmd +++ b/vignettes/custom-diagnostic.Rmd @@ -37,8 +37,11 @@ check_spelling = function(message_data) { aspell_dir <- file.path(tempdir(), 'aspell') dir.create(aspell_dir) original_dir <- setwd(aspell_dir) - on.exit({unlink(aspell_dir, recursive = TRUE); setwd(original_dir)}) - + on.exit({ + unlink(aspell_dir, recursive = TRUE) + setwd(original_dir) + }) + # (!is_repeat) makes sure we only check duplicate messages once # plural messages are in a list, so handle them separately message_data[(!is_repeat), by = .(file, type), { @@ -47,7 +50,7 @@ check_spelling = function(message_data) { # aspell() results has 5 columns: Original, File, Line, Column, Suggestions; we only need 1 & 5 results = utils::aspell(.BY$file) unlink(.BY$file) - + typo_idx <- sapply(results$Original, grep, msgid) # take the first suggestion replacement = sapply( @@ -60,7 +63,7 @@ check_spelling = function(message_data) { ) } ) - + .( call = call[typo_idx], file = file[typo_idx], @@ -73,7 +76,7 @@ check_spelling = function(message_data) { cat(all_msgid, file = .BY$file, sep = "\n") results = utils::aspell(.BY$file) unlink(.BY$file) - + # odd numbers in grep output --> first entry for each plural_msgid; even numbers --> second entry. # do this arithmetic trick to re-map that to the original entry number in msgid_plural typo_idx <- ((sapply(results$Original, grep, all_msgid) - 1L) %/% 2L) + 1L diff --git a/vignettes/developers.Rmd b/vignettes/developers.Rmd index 44786e55..dac95f5a 100644 --- a/vignettes/developers.Rmd +++ b/vignettes/developers.Rmd @@ -79,10 +79,10 @@ messagef <- function(fmt, ..., appendLF = TRUE) { warningf <- function(fmt, ..., immediate. = FALSE, noBreaks. = FALSE) { msg <- gettextf(fmt, ..., domain = "R-{mypackage}") - warning(msg, - domain = NA, - call. = FALSE, - immediate. = immediate., + warning(msg, + domain = NA, + call. = FALSE, + immediate. = immediate., noBreaks. = noBreaks. ) } @@ -288,8 +288,8 @@ So you might be tempted to construct a sentence like this: ```{r} cows <- function(n) { if (n == 1) { - paste0(n, " cow") - } else { + paste0(n, " cow") + } else { paste0(n, " cows") } } @@ -323,7 +323,7 @@ So we need to use a different helper: `ngettext(n, singular, plural)`: ```{r} field_cows <- function(n) { - glue(ngettxt(n, + glue(ngettext(n, "There is {n} cow in the field", "There are {n} cows in the field" ))