From cc2a53507fad06c3b4c5a7370f1179fcef21bd11 Mon Sep 17 00:00:00 2001 From: Hadley Wickham <h.wickham@gmail.com> Date: Mon, 1 Nov 2021 07:31:25 -0500 Subject: [PATCH 01/19] Export dev_compile() This function was previously called `update_mo_files()`. I don't know if `dev_compile()` is better, but I like the idea of using prefixes to distinguish between functions for developers and translators. Let me know if you have a better name, or if you'd prefer me to leave as is. I also made a few changes to make it more user friendly: it now discovers the package name (if needed), and can lazily recompile just the files that have changed. I also tweaked the messaging so you get one message per translation (which makes it easier to see what the msgfmt messages apply to). Two questions: * Is my scoped use of roxygen2 ok? (roxygen2 is designed to work like this so there's no chance of it clobbering your hand written files) * Do you want me to add tests? If so, I'd probably start by refactoring the code to extract out a data frame (data table?) that contains the po path, mo_path, and language. --- DESCRIPTION | 1 + NAMESPACE | 2 ++ R/msgmerge.R | 48 +++++++++++++++++++------ R/translate_package.R | 3 +- R/utils.R | 11 ++++++ man/dev_compile.Rd | 24 +++++++++++++ tests/testthat/test-translate-package.R | 2 +- 7 files changed, 77 insertions(+), 14 deletions(-) create mode 100644 man/dev_compile.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 32f2369a..f7c659fa 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -14,3 +14,4 @@ URL: https://github.com/MichaelChirico/potools BugReports: https://github.com/MichaelChirico/potools/issues Encoding: UTF-8 VignetteBuilder: knitr +RoxygenNote: 7.1.2 diff --git a/NAMESPACE b/NAMESPACE index cfcc50ca..cf831cff 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -11,6 +11,8 @@ export(translate_package) export(get_message_data) export(write_po_file, po_metadata) +export(dev_compile) + export(check_cracked_messages, check_untranslated_cat, check_untranslated_src) export(check_potools_sys_reqs) diff --git a/R/msgmerge.R b/R/msgmerge.R index 1b703618..58563c87 100644 --- a/R/msgmerge.R +++ b/R/msgmerge.R @@ -15,6 +15,10 @@ run_msgmerge = function(po_file, pot_file) { run_msgfmt = function(po_file, mo_file, verbose) { use_stats <- if (verbose) '--statistics' else '' + + po_file <- path.expand(po_file) + mo_file <- path.expand(mo_file) + # See #218. Solaris msgfmt doesn't support -c or --statistics if (Sys.info()[["sysname"]] == "SunOS") { cmd = sprintf("msgfmt -o %s %s", shQuote(mo_file), shQuote(po_file)) # nocov @@ -31,21 +35,43 @@ run_msgfmt = function(po_file, mo_file, verbose) { return(invisible()) } -update_mo_files = function(dir, package, verbose) { - inst_dir <- file.path(dir, "inst", "po") +#' Compile `.po` files to `.mo` +#' +#' This function compiles the plain text `.po` files that translators work +#' to the binary `.mo` files that are installed with packages. In order for +#' translations to be accessible to R, you must re-compile every time the `.po` +#' files are changed. +#' +#' @param dir Path to package root directory. +#' @param package Name of package. If not supplied, read from `DESCRIPTION`. +#' @param verbose If `TRUE`, print information as it goes. +#' @param lazy If `TRUE`, only `.mo` functions that are older than `.po` +#' files be updated +dev_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { + if (is.null(package)) { + package <- get_desc_data(dir)[["Package"]] + } + + po_paths <- list.files(file.path(dir, "po"), pattern = "\\.po$", full.names = TRUE) + lang_regex <- "^(R-)?([a-zA-Z_]+)\\.po$" + languages <- gsub(lang_regex, "\\2", basename(po_paths)) + mo_names <- gsub(lang_regex, sprintf("\\1%s.mo", package), basename(po_paths)) + mo_paths <- file.path(dir, "inst", "po", languages, "LC_MESSAGES", mo_names) + dir_create(dirname(mo_paths)) - po_files <- list.files(file.path(dir, "po"), pattern = "\\.po$") - languages <- gsub(lang_regex, "\\2", po_files) - mo_files <- gsub(lang_regex, sprintf("\\1%s.mo", package), po_files) - mo_dirs <- file.path(inst_dir, languages, "LC_MESSAGES") - # NB: dir.create() only accepts one directory at a time... - for (mo_dir in unique(mo_dirs)) dir.create(mo_dir, recursive = TRUE, showWarnings = FALSE) + if (lazy) { + outdated <- is_outdated(po_paths, mo_paths) + po_paths <- po_paths[outdated] + mo_paths <- mo_paths[outdated] + languages <- languages[outdated] + } - for (ii in seq_along(po_files)) { + for (ii in seq_along(po_paths)) { + if (verbose) message("Recompiling ", languages[ii], " translation") run_msgfmt( - po_file = file.path(dir, "po", po_files[ii]), - mo_file = file.path(mo_dirs[ii], mo_files[ii]), + po_file = po_paths[ii], + mo_file = mo_paths[ii], verbose = verbose ) } diff --git a/R/translate_package.R b/R/translate_package.R index beee713f..9460aee9 100644 --- a/R/translate_package.R +++ b/R/translate_package.R @@ -211,9 +211,8 @@ translate_package = function( INCOMPLETE = FALSE } - if (verbose) message('"Installing" translations with msgfmt') # TODO: reinstate source marker tags, at least for src .pot file & maybe for R .pot file too? - update_mo_files(dir, package, verbose = verbose) + dev_compile(dir, package, verbose = verbose) return(invisible()) } diff --git a/R/utils.R b/R/utils.R index 8875f902..4b101a6b 100644 --- a/R/utils.R +++ b/R/utils.R @@ -153,3 +153,14 @@ get_lang_metadata = function(language) { } update_metadata(language) } + +# Vectorised version of dir.create +dir_create <- function(dirs) { + for (dir in unique(dirs)) { + dir.create(dir, recursive = TRUE, showWarnings = FALSE) + } +} + +is_outdated <- function(src, dst) { + ifelse(!file.exists(dst), TRUE, file.mtime(src) > file.mtime(dst)) +} diff --git a/man/dev_compile.Rd b/man/dev_compile.Rd new file mode 100644 index 00000000..7cd450dc --- /dev/null +++ b/man/dev_compile.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/msgmerge.R +\name{dev_compile} +\alias{dev_compile} +\title{Compile `.po` files to `.mo`} +\usage{ +dev_compile(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) +} +\arguments{ +\item{dir}{Path to package root directory.} + +\item{package}{Name of package. If not supplied, read from `DESCRIPTION`.} + +\item{lazy}{If `TRUE`, only `.mo` functions that are older than `.po` +files be updated} + +\item{verbose}{If `TRUE`, print information as it goes.} +} +\description{ +This function compiles the plain text `.po` files that translators work +to the binary `.mo` files that are installed with packages. In order for +translations to be accessible to R, you must re-compile every time the `.po` +files are changed. +} diff --git a/tests/testthat/test-translate-package.R b/tests/testthat/test-translate-package.R index ce2535e7..4fa187e5 100644 --- a/tests/testthat/test-translate-package.R +++ b/tests/testthat/test-translate-package.R @@ -69,7 +69,7 @@ test_that("translate_package works on a simple package", { { expect_messages( translate_package(pkg, "zh_CN", verbose=TRUE), - c("Beginning new translations", "BEGINNING TRANSLATION", '"Installing" translations with msgfmt'), + c("Beginning new translations", "BEGINNING TRANSLATION", "Generating en@quot translations"), fixed = TRUE ) From 3a28abbea1fc0a950e3f01c5b11467760b0bbefc Mon Sep 17 00:00:00 2001 From: Hadley Wickham <h.wickham@gmail.com> Date: Thu, 4 Nov 2021 08:04:43 -0500 Subject: [PATCH 02/19] Rename dev_compile() to po_compile() --- NAMESPACE | 2 +- R/msgmerge.R | 9 ++++----- R/translate_package.R | 2 +- man/{dev_compile.Rd => po_compile.Rd} | 13 ++++++------- 4 files changed, 12 insertions(+), 14 deletions(-) rename man/{dev_compile.Rd => po_compile.Rd} (63%) diff --git a/NAMESPACE b/NAMESPACE index cf831cff..04e6750d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -11,7 +11,7 @@ export(translate_package) export(get_message_data) export(write_po_file, po_metadata) -export(dev_compile) +export(po_compile) export(check_cracked_messages, check_untranslated_cat, check_untranslated_src) diff --git a/R/msgmerge.R b/R/msgmerge.R index 58563c87..1a09157d 100644 --- a/R/msgmerge.R +++ b/R/msgmerge.R @@ -37,17 +37,16 @@ run_msgfmt = function(po_file, mo_file, verbose) { #' Compile `.po` files to `.mo` #' -#' This function compiles the plain text `.po` files that translators work -#' to the binary `.mo` files that are installed with packages. In order for -#' translations to be accessible to R, you must re-compile every time the `.po` -#' files are changed. +#' This function compiles the plain text `.po` files that translators work with +#' in to the binary `.mo` files that are installed with packages and used for +#' live translations. #' #' @param dir Path to package root directory. #' @param package Name of package. If not supplied, read from `DESCRIPTION`. #' @param verbose If `TRUE`, print information as it goes. #' @param lazy If `TRUE`, only `.mo` functions that are older than `.po` #' files be updated -dev_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { +po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { if (is.null(package)) { package <- get_desc_data(dir)[["Package"]] } diff --git a/R/translate_package.R b/R/translate_package.R index 9460aee9..fd7bc0cc 100644 --- a/R/translate_package.R +++ b/R/translate_package.R @@ -212,7 +212,7 @@ translate_package = function( } # TODO: reinstate source marker tags, at least for src .pot file & maybe for R .pot file too? - dev_compile(dir, package, verbose = verbose) + po_compile(dir, package, verbose = verbose) return(invisible()) } diff --git a/man/dev_compile.Rd b/man/po_compile.Rd similarity index 63% rename from man/dev_compile.Rd rename to man/po_compile.Rd index 7cd450dc..59803d1b 100644 --- a/man/dev_compile.Rd +++ b/man/po_compile.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/msgmerge.R -\name{dev_compile} -\alias{dev_compile} +\name{po_compile} +\alias{po_compile} \title{Compile `.po` files to `.mo`} \usage{ -dev_compile(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) +po_compile(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) } \arguments{ \item{dir}{Path to package root directory.} @@ -17,8 +17,7 @@ files be updated} \item{verbose}{If `TRUE`, print information as it goes.} } \description{ -This function compiles the plain text `.po` files that translators work -to the binary `.mo` files that are installed with packages. In order for -translations to be accessible to R, you must re-compile every time the `.po` -files are changed. +This function compiles the plain text `.po` files that translators work with +in to the binary `.mo` files that are installed with packages and used for +live translations. } From a9872991353c3c7780280c155006962d988cede1 Mon Sep 17 00:00:00 2001 From: Hadley Wickham <h.wickham@gmail.com> Date: Thu, 4 Nov 2021 08:31:28 -0500 Subject: [PATCH 03/19] Pull out separate po_files() function --- R/msgmerge.R | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/R/msgmerge.R b/R/msgmerge.R index 1a09157d..dbe49a4b 100644 --- a/R/msgmerge.R +++ b/R/msgmerge.R @@ -47,6 +47,22 @@ run_msgfmt = function(po_file, mo_file, verbose) { #' @param lazy If `TRUE`, only `.mo` functions that are older than `.po` #' files be updated po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { + po_files <- po_files(dir = dir, package = package, lazy = lazy) + dir_create(dirname(po_files$mo)) + + for (ii in seq_len(nrow(po_files))) { + if (verbose) messagef("Recompiling %s translation", po_files$language[ii]) + run_msgfmt( + po_file = po_files$po[ii], + mo_file = po_files$mo[ii], + verbose = verbose + ) + } + + return(invisible()) +} + +po_files <- function(dir = ".", package = NULL, lazy = TRUE) { if (is.null(package)) { package <- get_desc_data(dir)[["Package"]] } @@ -57,25 +73,19 @@ po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { languages <- gsub(lang_regex, "\\2", basename(po_paths)) mo_names <- gsub(lang_regex, sprintf("\\1%s.mo", package), basename(po_paths)) mo_paths <- file.path(dir, "inst", "po", languages, "LC_MESSAGES", mo_names) - dir_create(dirname(mo_paths)) - if (lazy) { - outdated <- is_outdated(po_paths, mo_paths) - po_paths <- po_paths[outdated] - mo_paths <- mo_paths[outdated] - languages <- languages[outdated] - } + out <- data.frame( + language = languages, + po = po_paths, + mo = mo_paths, + stringsAsFactors = FALSE + ) - for (ii in seq_along(po_paths)) { - if (verbose) message("Recompiling ", languages[ii], " translation") - run_msgfmt( - po_file = po_paths[ii], - mo_file = mo_paths[ii], - verbose = verbose - ) + if (lazy) { + out <- out[is_outdated(out$po, out$mo), , drop = FALSE] } - return(invisible()) + out } update_en_quot_mo_files <- function(dir, verbose) { From 428171894c6a86819aa7a94628a59bcaa1655b7a Mon Sep 17 00:00:00 2001 From: Hadley Wickham <h.wickham@gmail.com> Date: Thu, 4 Nov 2021 08:31:41 -0500 Subject: [PATCH 04/19] If not lazy, delete .mo files without matching .po --- R/msgmerge.R | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/R/msgmerge.R b/R/msgmerge.R index dbe49a4b..a907d50c 100644 --- a/R/msgmerge.R +++ b/R/msgmerge.R @@ -50,6 +50,17 @@ po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { po_files <- po_files(dir = dir, package = package, lazy = lazy) dir_create(dirname(po_files$mo)) + if (!lazy) { + # Clear out all older translations + mo_dirs <- dir(file.path(dir, "inst", "po"), full.names = TRUE) + to_delete <- mo_dirs[!basename(mo_dirs) %in% c(po_files$language, "en@quot")] + + for (dir in to_delete) { + if (verbose) messagef("Deleting unmatched %s translation", basename(dir)) + unlink(dir, recursive = TRUE) + } + } + for (ii in seq_len(nrow(po_files))) { if (verbose) messagef("Recompiling %s translation", po_files$language[ii]) run_msgfmt( From 51e1f70f8006f652a1eba9847344260617d5fbb2 Mon Sep 17 00:00:00 2001 From: Michael Chirico <michaelchirico4@gmail.com> Date: Sat, 6 Nov 2021 14:36:25 -0700 Subject: [PATCH 05/19] typo --- R/msgmerge.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/msgmerge.R b/R/msgmerge.R index a907d50c..923595b5 100644 --- a/R/msgmerge.R +++ b/R/msgmerge.R @@ -38,7 +38,7 @@ run_msgfmt = function(po_file, mo_file, verbose) { #' Compile `.po` files to `.mo` #' #' This function compiles the plain text `.po` files that translators work with -#' in to the binary `.mo` files that are installed with packages and used for +#' into the binary `.mo` files that are installed with packages and used for #' live translations. #' #' @param dir Path to package root directory. From 2122d917943879b54198145a6f4bb81e7465ceef Mon Sep 17 00:00:00 2001 From: Michael Chirico <michaelchirico4@gmail.com> Date: Sat, 6 Nov 2021 14:41:32 -0700 Subject: [PATCH 06/19] simplify helper --- R/utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/utils.R b/R/utils.R index 4b101a6b..ffa6cacd 100644 --- a/R/utils.R +++ b/R/utils.R @@ -162,5 +162,5 @@ dir_create <- function(dirs) { } is_outdated <- function(src, dst) { - ifelse(!file.exists(dst), TRUE, file.mtime(src) > file.mtime(dst)) + !file.exists(dst) | (file.mtime(src) > file.mtime(dst)) } From 0426052a64aeb755e1f2dc3269e9feda3d92c811 Mon Sep 17 00:00:00 2001 From: Michael Chirico <michaelchirico4@gmail.com> Date: Sat, 6 Nov 2021 14:49:05 -0700 Subject: [PATCH 07/19] tweaks --- R/msgmerge.R | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/R/msgmerge.R b/R/msgmerge.R index 923595b5..e6dc5dbb 100644 --- a/R/msgmerge.R +++ b/R/msgmerge.R @@ -80,20 +80,19 @@ po_files <- function(dir = ".", package = NULL, lazy = TRUE) { po_paths <- list.files(file.path(dir, "po"), pattern = "\\.po$", full.names = TRUE) - lang_regex <- "^(R-)?([a-zA-Z_]+)\\.po$" + lang_regex <- "^(R-)?([a-z]{2}(?:_[A-Z]{2})?)\\.po$" languages <- gsub(lang_regex, "\\2", basename(po_paths)) mo_names <- gsub(lang_regex, sprintf("\\1%s.mo", package), basename(po_paths)) mo_paths <- file.path(dir, "inst", "po", languages, "LC_MESSAGES", mo_names) - out <- data.frame( + out <- data.table( language = languages, po = po_paths, - mo = mo_paths, - stringsAsFactors = FALSE + mo = mo_paths ) if (lazy) { - out <- out[is_outdated(out$po, out$mo), , drop = FALSE] + out <- out[is_outdated(po, mo)] } out From 1250bff4e3b6176c419283c3ab8cf02da42f2e95 Mon Sep 17 00:00:00 2001 From: Michael Chirico <michaelchirico4@gmail.com> Date: Sat, 6 Nov 2021 14:49:50 -0700 Subject: [PATCH 08/19] match signature order --- R/msgmerge.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/msgmerge.R b/R/msgmerge.R index e6dc5dbb..ba6cf5cf 100644 --- a/R/msgmerge.R +++ b/R/msgmerge.R @@ -43,9 +43,9 @@ run_msgfmt = function(po_file, mo_file, verbose) { #' #' @param dir Path to package root directory. #' @param package Name of package. If not supplied, read from `DESCRIPTION`. -#' @param verbose If `TRUE`, print information as it goes. #' @param lazy If `TRUE`, only `.mo` functions that are older than `.po` #' files be updated +#' @param verbose If `TRUE`, print information as it goes. po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { po_files <- po_files(dir = dir, package = package, lazy = lazy) dir_create(dirname(po_files$mo)) From 013b749f5ddf2a56dcac15711af7d3d0cdc9ef20 Mon Sep 17 00:00:00 2001 From: Michael Chirico <michaelchirico4@gmail.com> Date: Sat, 6 Nov 2021 14:58:24 -0700 Subject: [PATCH 09/19] clarify verbose message --- R/msgmerge.R | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/R/msgmerge.R b/R/msgmerge.R index ba6cf5cf..94f6ea91 100644 --- a/R/msgmerge.R +++ b/R/msgmerge.R @@ -56,7 +56,10 @@ po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { to_delete <- mo_dirs[!basename(mo_dirs) %in% c(po_files$language, "en@quot")] for (dir in to_delete) { - if (verbose) messagef("Deleting unmatched %s translation", basename(dir)) + if (verbose) messagef( + "Found a compiled translation for %s at %s, but no corresponding .po; deleting", + basename(dir), dirname(dir) + ) unlink(dir, recursive = TRUE) } } From 28ab5b44b018ece6d59fb21a08af83e88c8c3267 Mon Sep 17 00:00:00 2001 From: Michael Chirico <michaelchirico4@gmail.com> Date: Sat, 6 Nov 2021 15:01:42 -0700 Subject: [PATCH 10/19] rename po_files --> get_po_metadata feels less muddled for describing the output (which involves .mo files) --- R/msgmerge.R | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/R/msgmerge.R b/R/msgmerge.R index 94f6ea91..7b1a3fa2 100644 --- a/R/msgmerge.R +++ b/R/msgmerge.R @@ -47,13 +47,13 @@ run_msgfmt = function(po_file, mo_file, verbose) { #' files be updated #' @param verbose If `TRUE`, print information as it goes. po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { - po_files <- po_files(dir = dir, package = package, lazy = lazy) - dir_create(dirname(po_files$mo)) + po_metadata <- get_po_metadata(dir = dir, package = package, lazy = lazy) + dir_create(dirname(po_metadata$mo)) if (!lazy) { # Clear out all older translations mo_dirs <- dir(file.path(dir, "inst", "po"), full.names = TRUE) - to_delete <- mo_dirs[!basename(mo_dirs) %in% c(po_files$language, "en@quot")] + to_delete <- mo_dirs[!basename(mo_dirs) %in% c(po_metadata$language, "en@quot")] for (dir in to_delete) { if (verbose) messagef( @@ -64,11 +64,11 @@ po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { } } - for (ii in seq_len(nrow(po_files))) { - if (verbose) messagef("Recompiling %s translation", po_files$language[ii]) + for (ii in seq_len(nrow(po_metadata))) { + if (verbose) messagef("Recompiling %s translation", po_metadata$language[ii]) run_msgfmt( - po_file = po_files$po[ii], - mo_file = po_files$mo[ii], + po_file = po_metadata$po[ii], + mo_file = po_metadata$mo[ii], verbose = verbose ) } @@ -76,7 +76,7 @@ po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { return(invisible()) } -po_files <- function(dir = ".", package = NULL, lazy = TRUE) { +get_po_metadata <- function(dir = ".", package = NULL, lazy = TRUE) { if (is.null(package)) { package <- get_desc_data(dir)[["Package"]] } From 0e8f56b29d9cb6686457b8ff1be9de530b4444e5 Mon Sep 17 00:00:00 2001 From: Hadley Wickham <h.wickham@gmail.com> Date: Sun, 7 Nov 2021 08:16:15 -0600 Subject: [PATCH 11/19] get_po_metadata() tweaks * Hoist lazy up into po_compile() * Use lang-regexp for filtering files * Record po type --- R/msgmerge.R | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/R/msgmerge.R b/R/msgmerge.R index 7b1a3fa2..c6482481 100644 --- a/R/msgmerge.R +++ b/R/msgmerge.R @@ -47,10 +47,12 @@ run_msgfmt = function(po_file, mo_file, verbose) { #' files be updated #' @param verbose If `TRUE`, print information as it goes. po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { - po_metadata <- get_po_metadata(dir = dir, package = package, lazy = lazy) + po_metadata <- get_po_metadata(dir = dir, package = package) dir_create(dirname(po_metadata$mo)) - if (!lazy) { + if (lazy) { + po_metadata <- po_metadata[is_outdated(po, mo)] + } else { # Clear out all older translations mo_dirs <- dir(file.path(dir, "inst", "po"), full.names = TRUE) to_delete <- mo_dirs[!basename(mo_dirs) %in% c(po_metadata$language, "en@quot")] @@ -76,29 +78,26 @@ po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { return(invisible()) } -get_po_metadata <- function(dir = ".", package = NULL, lazy = TRUE) { +get_po_metadata <- function(dir = ".", package = NULL) { if (is.null(package)) { package <- get_desc_data(dir)[["Package"]] } - po_paths <- list.files(file.path(dir, "po"), pattern = "\\.po$", full.names = TRUE) - lang_regex <- "^(R-)?([a-z]{2}(?:_[A-Z]{2})?)\\.po$" + po_paths <- list.files(file.path(dir, "po"), pattern = lang_regex, full.names = TRUE) + languages <- gsub(lang_regex, "\\2", basename(po_paths)) + type <- ifelse(grepl("^(R-)", basename(po_paths)), "R", "src") + mo_names <- gsub(lang_regex, sprintf("\\1%s.mo", package), basename(po_paths)) mo_paths <- file.path(dir, "inst", "po", languages, "LC_MESSAGES", mo_names) - out <- data.table( + data.table( language = languages, + type = type, po = po_paths, mo = mo_paths ) - - if (lazy) { - out <- out[is_outdated(po, mo)] - } - - out } update_en_quot_mo_files <- function(dir, verbose) { From 47a6a0838663bad6828150258d60f3e96ad06bbb Mon Sep 17 00:00:00 2001 From: Hadley Wickham <h.wickham@gmail.com> Date: Sun, 7 Nov 2021 08:18:11 -0600 Subject: [PATCH 12/19] Message about translation type --- R/msgmerge.R | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/R/msgmerge.R b/R/msgmerge.R index c6482481..824aff78 100644 --- a/R/msgmerge.R +++ b/R/msgmerge.R @@ -67,10 +67,11 @@ po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { } for (ii in seq_len(nrow(po_metadata))) { - if (verbose) messagef("Recompiling %s translation", po_metadata$language[ii]) + row <- po_metadata[ii] + if (verbose) messagef("Recompiling '%s' %s translation", row$language, row$type) run_msgfmt( - po_file = po_metadata$po[ii], - mo_file = po_metadata$mo[ii], + po_file = row$po, + mo_file = row$mo, verbose = verbose ) } From fb69e90f43365af1807a5ab3c65ee32921703523 Mon Sep 17 00:00:00 2001 From: Hadley Wickham <h.wickham@gmail.com> Date: Sun, 7 Nov 2021 08:24:20 -0600 Subject: [PATCH 13/19] Move po_compile() to own file --- R/msgmerge.R | 64 -------------------------------------------- R/po_compile.R | 67 +++++++++++++++++++++++++++++++++++++++++++++++ man/po_compile.Rd | 4 +-- potools.Rproj | 1 + 4 files changed, 70 insertions(+), 66 deletions(-) create mode 100644 R/po_compile.R diff --git a/R/msgmerge.R b/R/msgmerge.R index 824aff78..47c2d37b 100644 --- a/R/msgmerge.R +++ b/R/msgmerge.R @@ -35,71 +35,7 @@ run_msgfmt = function(po_file, mo_file, verbose) { return(invisible()) } -#' Compile `.po` files to `.mo` -#' -#' This function compiles the plain text `.po` files that translators work with -#' into the binary `.mo` files that are installed with packages and used for -#' live translations. -#' -#' @param dir Path to package root directory. -#' @param package Name of package. If not supplied, read from `DESCRIPTION`. -#' @param lazy If `TRUE`, only `.mo` functions that are older than `.po` -#' files be updated -#' @param verbose If `TRUE`, print information as it goes. -po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { - po_metadata <- get_po_metadata(dir = dir, package = package) - dir_create(dirname(po_metadata$mo)) - if (lazy) { - po_metadata <- po_metadata[is_outdated(po, mo)] - } else { - # Clear out all older translations - mo_dirs <- dir(file.path(dir, "inst", "po"), full.names = TRUE) - to_delete <- mo_dirs[!basename(mo_dirs) %in% c(po_metadata$language, "en@quot")] - - for (dir in to_delete) { - if (verbose) messagef( - "Found a compiled translation for %s at %s, but no corresponding .po; deleting", - basename(dir), dirname(dir) - ) - unlink(dir, recursive = TRUE) - } - } - - for (ii in seq_len(nrow(po_metadata))) { - row <- po_metadata[ii] - if (verbose) messagef("Recompiling '%s' %s translation", row$language, row$type) - run_msgfmt( - po_file = row$po, - mo_file = row$mo, - verbose = verbose - ) - } - - return(invisible()) -} - -get_po_metadata <- function(dir = ".", package = NULL) { - if (is.null(package)) { - package <- get_desc_data(dir)[["Package"]] - } - - lang_regex <- "^(R-)?([a-z]{2}(?:_[A-Z]{2})?)\\.po$" - po_paths <- list.files(file.path(dir, "po"), pattern = lang_regex, full.names = TRUE) - - languages <- gsub(lang_regex, "\\2", basename(po_paths)) - type <- ifelse(grepl("^(R-)", basename(po_paths)), "R", "src") - - mo_names <- gsub(lang_regex, sprintf("\\1%s.mo", package), basename(po_paths)) - mo_paths <- file.path(dir, "inst", "po", languages, "LC_MESSAGES", mo_names) - - data.table( - language = languages, - type = type, - po = po_paths, - mo = mo_paths - ) -} update_en_quot_mo_files <- function(dir, verbose) { pot_files <- list.files(file.path(dir, "po"), pattern = "\\.pot$", full.names = TRUE) diff --git a/R/po_compile.R b/R/po_compile.R new file mode 100644 index 00000000..42eec5b4 --- /dev/null +++ b/R/po_compile.R @@ -0,0 +1,67 @@ +#' Compile `.po` files to `.mo` +#' +#' This function compiles the plain text `.po` files that translators work with +#' into the binary `.mo` files that are installed with packages and used for +#' live translations. +#' +#' @param dir Path to package root directory. +#' @param package Name of package. If not supplied, read from `DESCRIPTION`. +#' @param lazy If `TRUE`, only `.mo` functions that are older than `.po` +#' files be updated +#' @param verbose If `TRUE`, print information as it goes. +po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { + po_metadata <- get_po_metadata(dir = dir, package = package) + dir_create(dirname(po_metadata$mo)) + + if (lazy) { + po_metadata <- po_metadata[is_outdated(po, mo)] + } else { + # Clear out all older translations + mo_dirs <- dir(file.path(dir, "inst", "po"), full.names = TRUE) + to_delete <- mo_dirs[!basename(mo_dirs) %in% c(po_metadata$language, "en@quot")] + + for (dir in to_delete) { + if (verbose) messagef( + "Found a compiled translation for %s at %s, but no corresponding .po; deleting", + basename(dir), dirname(dir) + ) + unlink(dir, recursive = TRUE) + } + } + + for (ii in seq_len(nrow(po_metadata))) { + row <- po_metadata[ii] + if (verbose) messagef("Recompiling '%s' %s translation", row$language, row$type) + run_msgfmt( + po_file = row$po, + mo_file = row$mo, + verbose = verbose + ) + } + + return(invisible()) +} + + +get_po_metadata <- function(dir = ".", package = NULL) { + if (is.null(package)) { + package <- get_desc_data(dir)[["Package"]] + } + + lang_regex <- "^(R-)?([a-z]{2}(?:_[A-Z]{2})?)\\.po$" + lang_regex <- "^(R-)?([a-z]{2}(?:_[A-Z]{2})?)\\.po$" + po_paths <- list.files(file.path(dir, "po"), pattern = lang_regex, full.names = TRUE) + + languages <- gsub(lang_regex, "\\2", basename(po_paths)) + type <- ifelse(grepl("^(R-)", basename(po_paths)), "R", "src") + + mo_names <- gsub(lang_regex, sprintf("\\1%s.mo", package), basename(po_paths)) + mo_paths <- file.path(dir, "inst", "po", languages, "LC_MESSAGES", mo_names) + + data.table( + language = languages, + type = type, + po = po_paths, + mo = mo_paths + ) +} diff --git a/man/po_compile.Rd b/man/po_compile.Rd index 59803d1b..a9ee75ca 100644 --- a/man/po_compile.Rd +++ b/man/po_compile.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/msgmerge.R +% Please edit documentation in R/po_compile.R \name{po_compile} \alias{po_compile} \title{Compile `.po` files to `.mo`} @@ -18,6 +18,6 @@ files be updated} } \description{ This function compiles the plain text `.po` files that translators work with -in to the binary `.mo` files that are installed with packages and used for +into the binary `.mo` files that are installed with packages and used for live translations. } diff --git a/potools.Rproj b/potools.Rproj index 497f8bfc..9ecfdfcb 100644 --- a/potools.Rproj +++ b/potools.Rproj @@ -18,3 +18,4 @@ StripTrailingWhitespace: Yes BuildType: Package PackageUseDevtools: Yes PackageInstallArgs: --no-multiarch --with-keep.source +PackageRoxygenize: rd From 23f7460d13502354101f927c8a87b01768a5b094 Mon Sep 17 00:00:00 2001 From: Hadley Wickham <h.wickham@gmail.com> Date: Sun, 7 Nov 2021 08:34:28 -0600 Subject: [PATCH 14/19] Tweak test check --- tests/testthat/test-translate-package.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-translate-package.R b/tests/testthat/test-translate-package.R index 4fa187e5..ed4c3b07 100644 --- a/tests/testthat/test-translate-package.R +++ b/tests/testthat/test-translate-package.R @@ -69,7 +69,7 @@ test_that("translate_package works on a simple package", { { expect_messages( translate_package(pkg, "zh_CN", verbose=TRUE), - c("Beginning new translations", "BEGINNING TRANSLATION", "Generating en@quot translations"), + c("Beginning new translations", "BEGINNING TRANSLATION", "Recompiling 'zh_CN' R translation"), fixed = TRUE ) From e5b8cdcd5a5a1fed65f67f08ccb4b62c2e440be4 Mon Sep 17 00:00:00 2001 From: Hadley Wickham <h.wickham@gmail.com> Date: Sun, 7 Nov 2021 08:36:51 -0600 Subject: [PATCH 15/19] Add a test for get_po_metadata() --- DESCRIPTION | 2 +- tests/testthat/test-po_compile.R | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 tests/testthat/test-po_compile.R diff --git a/DESCRIPTION b/DESCRIPTION index f7c659fa..985fa86e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -5,7 +5,7 @@ Version: 0.2.3 Author: Michael Chirico Depends: R (>= 4.0.0) Imports: data.table -Suggests: crayon, knitr, rmarkdown, testthat +Suggests: crayon, knitr, rmarkdown, testthat, withr SystemRequirements: gettext Maintainer: Michael Chirico <MichaelChirico4@gmail.com> Description: Translating messages in R packages is managed using the po top-level directory and the 'gettext' program. This package provides some helper functions for building this support in R packages, e.g. common validation & I/O tasks. diff --git a/tests/testthat/test-po_compile.R b/tests/testthat/test-po_compile.R new file mode 100644 index 00000000..6ef6ced0 --- /dev/null +++ b/tests/testthat/test-po_compile.R @@ -0,0 +1,11 @@ +# metadata ---------------------------------------------------------------- + +test_that("can find R and src translations", { + temp <- withr::local_tempdir() + dir.create(file.path(temp, "po")) + file.create(file.path(temp, "po", c("R-en.po", "en.po"))) + + meta <- withr::with_dir(temp, get_po_metadata(package = "test")) + expect_equal(meta$language, c("en", "en")) + expect_setequal(meta$type, c("R", "src")) +}) From e5a862e3882b63bc5d758f35ba8500e38e06bef3 Mon Sep 17 00:00:00 2001 From: Hadley Wickham <h.wickham@gmail.com> Date: Sun, 7 Nov 2021 08:38:05 -0600 Subject: [PATCH 16/19] Improve variable access --- R/po_compile.R | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/R/po_compile.R b/R/po_compile.R index 42eec5b4..8008d942 100644 --- a/R/po_compile.R +++ b/R/po_compile.R @@ -14,7 +14,7 @@ po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { dir_create(dirname(po_metadata$mo)) if (lazy) { - po_metadata <- po_metadata[is_outdated(po, mo)] + po_metadata <- po_metadata[is_outdated(po_metadata$po, po_metadata$mo)] } else { # Clear out all older translations mo_dirs <- dir(file.path(dir, "inst", "po"), full.names = TRUE) @@ -32,11 +32,7 @@ po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { for (ii in seq_len(nrow(po_metadata))) { row <- po_metadata[ii] if (verbose) messagef("Recompiling '%s' %s translation", row$language, row$type) - run_msgfmt( - po_file = row$po, - mo_file = row$mo, - verbose = verbose - ) + run_msgfmt(row$po, row$mo, verbose = verbose) } return(invisible()) From 89158472f57e0f25c5f25f476a690094ef5d8265 Mon Sep 17 00:00:00 2001 From: Michael Chirico <michaelchirico4@gmail.com> Date: Sun, 7 Nov 2021 15:11:32 -0800 Subject: [PATCH 17/19] dup line --- R/po_compile.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/po_compile.R b/R/po_compile.R index 8008d942..26fd6691 100644 --- a/R/po_compile.R +++ b/R/po_compile.R @@ -44,7 +44,6 @@ get_po_metadata <- function(dir = ".", package = NULL) { package <- get_desc_data(dir)[["Package"]] } - lang_regex <- "^(R-)?([a-z]{2}(?:_[A-Z]{2})?)\\.po$" lang_regex <- "^(R-)?([a-z]{2}(?:_[A-Z]{2})?)\\.po$" po_paths <- list.files(file.path(dir, "po"), pattern = lang_regex, full.names = TRUE) From 8d0cceeb6db50f6e0f79bfd6220b82b48b2f6cd6 Mon Sep 17 00:00:00 2001 From: Michael Chirico <michaelchirico4@gmail.com> Date: Sun, 7 Nov 2021 15:26:51 -0800 Subject: [PATCH 18/19] startsWith over grepl --- R/po_compile.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/po_compile.R b/R/po_compile.R index 26fd6691..5c2ec206 100644 --- a/R/po_compile.R +++ b/R/po_compile.R @@ -48,7 +48,7 @@ get_po_metadata <- function(dir = ".", package = NULL) { po_paths <- list.files(file.path(dir, "po"), pattern = lang_regex, full.names = TRUE) languages <- gsub(lang_regex, "\\2", basename(po_paths)) - type <- ifelse(grepl("^(R-)", basename(po_paths)), "R", "src") + type <- ifelse(startsWith(basename(po_paths), "R-"), "R", "src") mo_names <- gsub(lang_regex, sprintf("\\1%s.mo", package), basename(po_paths)) mo_paths <- file.path(dir, "inst", "po", languages, "LC_MESSAGES", mo_names) From eff4ee5fb54c7526e618231699b0d487e38579c9 Mon Sep 17 00:00:00 2001 From: Hadley Wickham <h.wickham@gmail.com> Date: Mon, 8 Nov 2021 06:19:43 -0600 Subject: [PATCH 19/19] Use new form of get_desc_data() --- R/po_compile.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/po_compile.R b/R/po_compile.R index 5c2ec206..82394021 100644 --- a/R/po_compile.R +++ b/R/po_compile.R @@ -41,7 +41,7 @@ po_compile = function(dir = ".", package = NULL, lazy = TRUE, verbose = TRUE) { get_po_metadata <- function(dir = ".", package = NULL) { if (is.null(package)) { - package <- get_desc_data(dir)[["Package"]] + package <- get_desc_data(dir, "Package") } lang_regex <- "^(R-)?([a-z]{2}(?:_[A-Z]{2})?)\\.po$"