Closed hadley closed 3 years ago
This wouldn't require a run-time dependency on potools, because we'd just give the user some snippet to include in their package, like:
tr_ <- function(...) {
gettext(..., domain = "R-pkgdown")
}
We'd need to carefully think through what the arguments should be for compatibility with the maximum number of functions.
Normally you would flag translatable text with gettext()
and friends. But having wrapper functions that save typing and having to specify the domain each time is inevitable. I think that's what the data.table team did. For metR what I did was to create wrapper functions catf()
stopf()
, warningf()
and messagef()
similar to
warningf <- function (fmt, ..., call. = TRUE, immediate. = FALSE, noBreaks. = FALSE,
domain = "R-metR") {
x <- gettextf(fmt, ..., domain = domain)
if (isTRUE(call.)) {
call = sys.call(-1)
} else {
call = NULL
}
e <- simpleWarning(x, call = call)
warning(e)
}
Then, figuring out untranslatable message is as easy as searching the source code for "warning(", "message(", etc... and replacing them with "warningf(", etc..
For this to work with potools, you need to set the custom_translation_functions
argument like this:
potools::translate_package(custom_translation_functions = list(R = c("warningf:fmt|1")))
Perhaps having just one function like that is better since it centralises all the work into just one interface (or two, perhaps, for pluralization),
@eliocamp yeah, I was thinking that just using one function makes life easier since you don't need to create helpers for every messaging function.
I think custom_translation_functions is the way to go, but off the top of my head it only solves half of your proposal, because IIRC it is only supplementary, where your approach would also ignore the base messaging functions
of course we can pass domain=NA to skip translation, but it might be nice to provide a way to use only the custom functions.
PS of course your suggestion is much closer to the way translation is usually done with lib-gettext (e.g. R uses just _
instead of tr_
in C) -- it's just that R has already built tools to automatically recognize those base functions.
There's the tooling in potools, but there's also the recommended workflow. It just feels to me like most people will be better served by iteratively declaring which string should be translated, rather than assuming all calls to message()
, stop()
etc can be, just because the functions can already do translation.
I think my apprehension about that default is:
One other way forward could be to modularize a bit more -- rather than relying on translate_package()
to accomplish both things, we can provide a way for the main developer to take a pass over their messages & copy-edit all of them, and mark which ones are intended for translation in the process. Then we know all the messages will have been read & also non-translation strings are skipped.
Hope that makes sense. Happy to discuss further.
Yeah, that makes sense. I think explicitly flagging strings for translation makes the most sense for tidyverse packages (since we have the resources to systematically work through every error message), but for most packages just relying on stop()
and friends will give the biggest lift.
Resolved by po_extract()
's style
argument
I've been thinking about the process of adding translations to a package for the first time. Currently, the workflow will automatically extract text to be translated from
message()
,warning()
,error()
etc. I'm starting to wonder if that's the right approach, because the first time you do this, you're like to get a lot of suboptimal translations because the messages were not designed with translation in mind, and you really don't want to spend the limited time of translators of discovering poorly formed messages.I wonder if it would be better to adhere closer to the gettext approach and explicitly mark each translatable string with (e.g.)
tr_()
. That way you could systematically work through your package, seeing each message in context, and rewriting it for best translation style. This also works regardless of what functions you use to create messages, whether it's base'sstop()
, rlang'sabort()
, or some helper that you have created for your own package. It also works nicely withglue()
andsprintf()
. (We'd still need another helper for pluralisation. Maybetr_n(n, "singular", "plural")
?).You'd then need a new way to determine if you've missed any strings, or to figure out what you've worked on next. I think that's fairly straightforward — you just check that every call to
stop()
,warning()
,message()
and friends includes a call totr_()
.