Error rewrite#12556
Conversation
|
I like the idea of a more unified error handling approach. There is one thing worth considering: We've been trying to split up the main library crate. So far, only a bit of code has been extracted into |
| "%s: missing man page\nDocumentation may not be installed.\n`help %s` will show an online version" | ||
|
|
||
| /// Error message on multiple scope levels for variables. | ||
| pub GLOCAL |
There was a problem hiding this comment.
This looks like a typo. It's probably intended to combine global and local, but that's not obvious from the name alone, it does not account for the universal and function scopes, and doesn't communicate what's wrong. Maybe we can call it something like MULTIPLE_SCOPES. At the moment, this constant seems unused while BUILTIN_ERR_GLOCAL in src/builtins/shared.rs is still used.
I know that this isn't your naming choice. The name has been in the codebase for a while and I've also touched it before, but I only realized this naming now, so I suggest we improve it, although that can happen independently of this PR.
There was a problem hiding this comment.
At the moment, this constant seems unused while BUILTIN_ERR_GLOCAL in src/builtins/shared.rs is still used.
This PR is just an RFC for now. So Error contains all the values from shared.rs. Nearly all of them are unused right now. Some still contain the %s for a cmd/subcmd.
That said, I'm changing my approach to this PR. I'm doing a first pass/commit to standardize the messages themselves (e.g. extract the cmd/subcmd from the error msg, using nested append() instead). This includes revisiting the constant names among other things (e.g. MISSING should be MISSING_OPT_ARG, and UNEXP_ARG should be UNEXP_OPT_ARG... well, their BUILTIN_ERR_xxx equivalent in shared.rs).
There was a problem hiding this comment.
Improving the constant names seems like a good start.
Generally, having localized messages which are as complete as possible is desirable, since it gives most context and flexibility to translators. With the command, and in some cases subcommand, prefixes, I think it's mostly fine to split it up and have nested wgettext_fmt! calls.
There was a problem hiding this comment.
That said, I'm changing my approach to this PR. I'm doing a first pass/commit to standardize the messages themselves
Scratch that. This is just too redundant. Too many &wgettext_fmt!("%s: %s", ..., &wgettext_fmt!(...)) added for every messages. And constants need to be temporarily doubled (one with the "%s: " prefix, one without) until all call sites get updated. I can keep the renaming separate, but the splitting (and other tweaks, like lower-casing the initial letter) might as well be done while switching to Error.
| .err | ||
| .appendln(&wgettext_fmt!("%s: %s: %s", cmd, subcmd, &self.msg)); | ||
| } | ||
| (None, Some(_)) => unreachable!("cannot have `subcmd` without `cmd`"), |
There was a problem hiding this comment.
We could make this unrepresentable by changing the definition of Error such that cmd has type Option<(&'a wstr, Option<&'a wstr>)>, and subcmd no longer exists. Makes the definition harder to read, so it probably deserves a comment, but it prevents having a subcommand without a command by construction.
There was a problem hiding this comment.
I dislike this unreachable! as well.
At the same time Some(cmd, Some(subcmd)) (or let Some(cmd, _) = self.cmd below) starts to be unwieldy/awkward, especially given that subcmd without cmd is not publicly constructible.
Alternatively, it would be to replace (None, None) with (None, _) above, which makes some sense since subcmd without cmd could be seen as meaningless rather than an error.
There was a problem hiding this comment.
Yeah, it's a tradeoff. Matching on (None, _) might be a decent approach. It would hide incorrectly constructed Errors, but the interface is fairly straightforward, so I think the risk of the code being changed to allow such a construction is acceptably low.
There was a problem hiding this comment.
maybe
struct CommandName<'a> {
cmd: &'a wstr,
subcmd: Option<&'a wstr>,
}and then command_name: Option<CommandName<'a>>
|
I've updated 1/3 of the builtins (abbr-fg). |
|
All builtins have been converted. I'll see about improving the test coverage for errors before calling it "done" though. EDIT: for the record, there are 142 error sites (where |
|
Good cleanup.
I like the old formatting (without the colon) better, it seems more natural.
I think
@danielrainer if we add a IoStreams method as above, it shouldn't affect call sites. but I guess there's no need to do that yet.
I wonder if we should enforce that all errors are used,
given how often these builder methods are used, maybe we should drop the
yeah this should return STATUS_INVALID_ARGS
I wonder if we should introduce a
I guess that can be decided later as well?
if it's used a lot that might be okay.
could extract the
I wonder if we can improve the naming of the append methods.
I think we usually use lowercase fish |
I split it for easier code review, especially the localization part (ensuring the messages are moved or copied, instead of replaced with an empty translation). Initially I was also thinking it would be useful in case we need to revert specific builtins but 1) there is likely too much interdependence on the localization (a revert is likely going to cause tons of merge conflicts), 2) the PR has changed from the idea of homogenizing the error messages to just an improvement of the internal API, i.e. less user-facing changes, so I don't see a need to "revert" instead of "fix" anymore. So if the current commits have been reviewed, I'll squashed most of them for the final version.
Easy enough to change now :)
It wouldn't be
Easy enough, at least currently since the
What do you mean by exceptions?
I was thinking of APIs like
I wasn't sure about changing the user-facing API (well, more so than what I'm already doing, although changing a status code is more risky than changing a translatable error message), or even if that should be done in this PR. Thus the TODO.
Currently, my
Indeed, which is why I didn't push the question.
The main point for a
I'm not sure that macro would work, not a macro_rule at least. Macro_proc should but that's an even bigger problem.
ok
I noticed, but this PR is just changing the error infrastructure. My goal is to later followup with a more user-facing PR to homogenized the error messages (there are a several error message that mean the same thing but say/present it differently, especially regarding options and combo of options). |
I'm assuming you're referring to I don't think passing an |
|
> Probably there's too many exceptions and too little test coverage.
What do you mean by exceptions?
as in, places that don't use the error value (but only do `if let Ok(..)` or `match { Err(_) => ... }`.
#[must_use] sounds good (but I guess that's an unrelated change).
even if that should be done in this PR.
doesn't hurt, especially if it's a separate commit
So maybe the whole class should be move to the `builtins` directory instead of being in `src`, that way the `builtin` work will be in the name path.
yeah maybe do that for now, can always change it later
> > ```
> > "Fish does not have shell options. See `help %s`.",
> > ```
>
> I think we usually use lowercase fish
I noticed, but this PR is just changing the error infrastructure.
yes but you're also adding it to translatable messages. No big deal of course, since anyone can adjust any translations when making this small change to the English one.
|
|
The PR is now ready to fully review, and can be merged once #12603 is |
To homogenize error reporting format, use a new Error struct. Currently this is used for builtins and ensuring a common cmd/subcmd prefix. Part of #12556
|
@krobelus any particular reason the last two commits haven't been merged? Added a commit that should fix the spurious Cirrus error in |
5e0f425 to
0f6cbe1
Compare
no, I was just busy
this filename is weirdly ambiguous because one might think it represents an "error" builtin.
Good. I like the conciseness of "Error", and the "builtin" part is still visible in the "use" statement.
the branching between string literal / expr (LocalizableString) is not needed, we can simply pass on any expression, diff --git a/src/builtins/error.rs b/src/builtins/error.rs
index 37a55f86ab..04848e7ad5 100644
--- a/src/builtins/error.rs
+++ b/src/builtins/error.rs
@@ -8,48 +8,33 @@
};
use fish_widestring::wstr;
#[macro_export]
macro_rules! err_fmt {
(
- $string:literal // format string
- $(, $args:expr)* // list of expressions
- $(,)? // optional trailing comma
- ) => {
- $crate::builtins::error::Error::new(
- $crate::wgettext_fmt!($string, $($args),*).into()
- )
- };
- (
- $string:expr // format string (LocalizableString)
+ $string:expr // format string (literal or LocalizableString)
$(, $args:expr)* // list of expressions
$(,)? // optional trailing comma
) => {
$crate::builtins::error::Error::new(
$crate::wgettext_fmt!($string, $($args),*).into()
)
};
}
pub use err_fmt;
#[macro_export]
macro_rules! err_str {
(
- $string:literal // format string
+ $string:expr // format string (literal or LocalizableString)
$(,)? // optional trailing comma
) => {
$crate::builtins::error::Error::new(std::borrow::Cow::Borrowed($crate::wgettext!($string)))
};
- (
- $string:expr // format string (LocalizableString)
- $(,)? // optional trailing comma
- ) => {
- $crate::builtins::error::Error::new(std::borrow::Cow::Borrowed($crate::wgettext!(&$string)))
- };
}
pub use err_str;
/// Generate an `Error` from a string without localization. This is typically
/// for error messages from external sources (e.g. C `strerror()`)
#[macro_export]
macro_rules! err_raw {
I wonder if
I was wondering if this is the best name. What we mean here is either "not-localized" or "already localized". I don't have a better suggestion, so
Yeah; panic and unreachable communicate almost the same thing but I tend to agree that unreachable is better for assumptions that are true (barring a compiler bug etc.).
maybe we should
Good change.
When looking at
the How about making the names reflect the format string better (can be done later of course):
this code merge could be an independent commit, right?
maybe this should be called
In future, we could extract the "builtin history" and "delete" into cmd/subcmd calls.
I'm not sure if we should drop translations on the floor (and leave it to translators) just because we remove Something like this pseudo code:
we should translate this one for all languages (using Chinese-style colons where appropriate). |
Done
Done
I like the uniformity of
"raw" just means "unprocessed"/"as-is". For legacy C, that means "unconverted to Rust errors". Rust "raw string" (
Not needed after other changes I just made 😊
Done.
Squashed
Done. Went with
I thought it was too minor to warrant its own commit that's all, like the "panic!" vs "unreachable!" earlier.
I used
I had a script that tries to preserve the translations. However, it worked by removing the prefix if it's the same between the reference and translated string, which is not the case for Chinese. Script attached below for the record.
There is at least one new translation: "fish does not have shell options [...]" in translation update scriptimport polib
import copy
lang = ["de", "en", "es", "fr", "ja_JP", "pl", "pt_BR", "sv", "zh_CN", "zh_TW"]
prefixes = ["%s: ", "%s: %s: ", "%s %s: "]
zh_separator = ":"
for lang in lang:
pofile = polib.pofile(f"localization/po/{lang}.po.old")
# Add entries without prefix
for entry in pofile.translated_entries():
for prefix in prefixes:
if not entry.msgid.startswith(prefix):
continue
newid = entry.msgid.removeprefix(prefix)
if newid == "" or newid in prefixes:
continue
existing_entry = pofile.find(newid)
msgstr_prefix = prefix.replace(": ", zh_separator) if lang.startswith("zh") else prefix
if entry.msgstr.startswith(msgstr_prefix):
newstr = entry.msgstr.removeprefix(msgstr_prefix)
if existing_entry is None:
newentry = copy.copy(entry)
newentry.msgid = newid
newentry.msgstr = newstr
pofile.append(newentry)
elif existing_entry.translated():
if existing_entry.msgstr != newstr:
print(f"=== Bad match ({lang})\n- '{existing_entry.msgstr}'\n+ '{newstr}'")
pass
elif newid == newstr:
pass
else:
existing_entry.msgstr = newstr
# Add entries for removed prefixes
if lang.startswith("zh"):
for prefix in prefixes:
newid = f"{prefix}%s"
newstr = newid.replace(": ", zh_separator)
existing_entry = pofile.find(newid)
if existing_entry is None:
newentry = polib.POEntry(
msgid = newid,
msgstr = newstr
)
pofile.append(newentry)
elif existing_entry.translated():
if existing_entry.msgstr != newstr:
print(f"=== Bad match ({lang})\n- '{existing_entry.msgstr}'\n+ '{newstr}'")
pass
else:
existing_entry.msgstr = newstr
pofile.save(f"localization/po/{lang}.po") |
Depends on what you mean by that. Fluent would have names for all variables, which of course adds context, but it's the same context for all users of that message. I've also had a look at the translations and it seems that the translations with the same prefix as the English messages were already updated. This leaves the Chinese messages, which seem to be handled in the script you just posted, but also French messages, which use a non-breaking space before the colon. (Except for #12617.) I could post code for updating the French translations, but it's probably more convenient for you to integrate the changes yourself. In case it helps, here's a diff to be applied upon "error rewrite: use new diff --git a/localization/po/fr.po b/localization/po/fr.po
index 66c1391225..b0ac8631f0 100644
--- a/localization/po/fr.po
+++ b/localization/po/fr.po
@@ -242,11 +242,11 @@
#, c-format
msgid "%s option requires %s"
-msgstr ""
+msgstr "l’option %s doit être utilisée avec %s"
#, c-format
msgid "%s requires a non-empty string"
-msgstr ""
+msgstr "%s doit être utilisée avec une chaîne non-vide"
#, c-format
msgid "%s variable"
@@ -342,11 +342,11 @@
#, c-format
msgid "%s: cannot use reserved keyword as function name"
-msgstr ""
+msgstr "%s : impossible d’utiliser un mot-clé réservé comme nom de fonction"
#, c-format
msgid "%s: contains a syntax error"
-msgstr ""
+msgstr "%s : contient une erreur de syntaxe"
#, c-format
msgid "%s: expected %d arguments; got %d"
@@ -370,7 +370,7 @@
#, c-format
msgid "%s: invalid base value"
-msgstr ""
+msgstr "%s : valeur de base invalide"
#, c-format
msgid "%s: invalid conversion specification"
@@ -378,7 +378,7 @@
#, c-format
msgid "%s: invalid function name"
-msgstr ""
+msgstr "%s : nom de fonction invalide"
#, c-format
msgid "%s: invalid integer"
@@ -386,7 +386,7 @@
#, c-format
msgid "%s: invalid mode"
-msgstr ""
+msgstr "%s : mode invalide"
#, c-format
msgid "%s: invalid mode name. See `help %s`"
@@ -406,7 +406,7 @@
#, c-format
msgid "%s: invalid scale"
-msgstr ""
+msgstr "%s : échelle invalide"
#, c-format
msgid "%s: invalid subcommand"
@@ -414,7 +414,7 @@
#, c-format
msgid "%s: invalid variable name. See `help %s`"
-msgstr ""
+msgstr "%s : nom de variable invalide. Voir « help %s »"
#, c-format
msgid "%s: missing argument"
@@ -450,7 +450,7 @@
#, c-format
msgid "%s: unexpected positional argument"
-msgstr ""
+msgstr "%s : argument positionnel inattendu"
#, c-format
msgid "%s: unknown option"
@@ -462,23 +462,23 @@
#, c-format
msgid "'%s' is a broken symbolic link to '%s'"
-msgstr ""
+msgstr "« %s » est un lien symbolique cassé vers « %s »"
#, c-format
msgid "'%s' is not a directory"
-msgstr ""
+msgstr "« %s » n’est pas un dossier"
#, c-format
msgid "'%s' is not a job"
-msgstr ""
+msgstr "« %s » n’est pas une tâche"
#, c-format
msgid "'%s' is not a valid job ID"
-msgstr ""
+msgstr "« %s » n’est pas un ID de tâche valide"
#, c-format
msgid "'%s' is not a valid process ID"
-msgstr ""
+msgstr "« %s » n’est pas un ID de processus valide"
msgid "'break' while not inside of loop"
msgstr "« break » hors d’une boucle"
@@ -532,7 +532,7 @@
msgstr ""
msgid "--command cannot be combined with --position=command"
-msgstr ""
+msgstr "--command ne peut pas être utilisée avec l’option --position=command"
msgid "--end and --length are mutually exclusive"
msgstr ""
@@ -550,7 +550,7 @@
msgstr ""
msgid "--set-cursor argument cannot be empty"
-msgstr ""
+msgstr "l’option --set-cursor ne peut pas être vide"
msgid "--tokens options are mutually exclusive"
msgstr ""
@@ -564,11 +564,11 @@
#, c-format
msgid "Abbreviation %s already exists, cannot rename %s"
-msgstr ""
+msgstr "l’abréviation %s existe déjà ; impossible de renommer %s"
#, c-format
msgid "Abbreviation '%s' cannot have spaces in the word"
-msgstr ""
+msgstr "l’abréviation « %s » ne peut pas avoir d’espaces dans son nom"
msgid "Abbreviation expansion"
msgstr ""
@@ -594,17 +594,17 @@
msgstr "Toujours"
msgid "Ambiguous job"
-msgstr ""
+msgstr "Tâche ambiguë"
msgid "An error occurred while setting up pipe"
msgstr "Une erreur est survenue lors du paramétrage du tube"
msgid "An option spec must have at least a short or a long flag"
-msgstr ""
+msgstr "Une spécification d’option doit avoir au moins un nom court ou long"
#, c-format
msgid "Argument '%s' is out of range"
-msgstr ""
+msgstr "L’argument « %s » est hors limites"
#, c-format
msgid "Argument is not a number: '%s'"
@@ -644,18 +644,18 @@
msgstr ""
msgid "Can not specify scope when removing block"
-msgstr ""
+msgstr "Ne peut pas indiquer la portée en enlevant le bloc"
msgid "Can not use the no-execute mode when running an interactive session"
msgstr "Le mode no-execute ne peut pas être utilisé dans une session interactive"
#, c-format
msgid "Can't put job %d, '%s' to foreground because it is not under job control"
-msgstr ""
+msgstr "Impossible de mettre la tâche %d, « %s » au premier plan, car elle n’est pas gérée par le contrôleur de tâches"
#, c-format
msgid "Can't put job %s, '%s' to background because it is not under job control"
-msgstr ""
+msgstr "Impossible de mettre la tâche %s, « %s » en arrière plan, car elle n’est pas gérée par le contrôleur de tâches"
#, c-format
msgid "Cannot add control modifier to control character '%s'"
@@ -663,19 +663,19 @@
#, c-format
msgid "Cannot combine options %s"
-msgstr ""
+msgstr "Impossible de combiner les options %s"
msgid "Cannot specify multiple positions"
-msgstr ""
+msgstr "Impossible de spécifier plusieurs positions"
msgid "Cannot specify multiple regex patterns"
-msgstr ""
+msgstr "Impossible de spécifier plusieurs expressions régulières"
msgid "Cannot specify multiple set-cursor options"
-msgstr ""
+msgstr "Impossible de spécifier plusieurs options set-cursor"
msgid "Cannot use --append or --prepend when assigning to a slice"
-msgstr ""
+msgstr "Impossible d’utiliser --append ou --prepend lors de l’assignation à une tranche"
msgid "Cannot use stdin (fd 0) as pipe output"
msgstr "Impossible d’utiliser l’entrée standard (fd 0) comme sortie de tube"
@@ -708,7 +708,7 @@
msgstr ""
msgid "Command not valid at an interactive prompt"
-msgstr ""
+msgstr "Commande invalide dans une invite interactive"
msgid "Commandname was invalid"
msgstr ""
@@ -724,18 +724,18 @@
#, c-format
msgid "Could not find '%s'"
-msgstr ""
+msgstr "Impossible de trouver « %s »"
#, c-format
msgid "Could not find a job with process ID '%d'"
-msgstr ""
+msgstr "Impossible de trouver une tâche avec l’ID de processus « %d »"
#, c-format
msgid "Could not find child processes with the name '%s'"
-msgstr ""
+msgstr "Impossible de trouver des processus enfants avec le nom « %s »"
msgid "Could not find home directory"
-msgstr ""
+msgstr "Dossier personnel introuvable"
#, c-format
msgid "Could not find job '%d'"
@@ -775,7 +775,7 @@
#, c-format
msgid "Did you mean `set %s %s`?"
-msgstr ""
+msgstr "Vouliez-vous dire « set %s %s » ?"
msgid "Division by zero"
msgstr ""
@@ -788,14 +788,14 @@
#, c-format
msgid "Empty directory '%s' does not exist"
-msgstr ""
+msgstr "Le dossier vide « %s » n’existe pas"
msgid "End a block of commands"
msgstr "Termine un bloc de commandes"
#, c-format
msgid "Error encountered while sourcing file '%s':"
-msgstr ""
+msgstr "Erreur lors de l’inclusion du fichier « %s »"
#, c-format
msgid "Error reading script file '%s':"
@@ -811,7 +811,7 @@
#, c-format
msgid "Error while reading file '%s'"
-msgstr ""
+msgstr "Erreur lors de la lecture du fichier « %s »"
#, c-format
msgid "Error: %s"
@@ -849,7 +849,7 @@
#, c-format
msgid "Expected %s for %s"
-msgstr ""
+msgstr "Seul %s est attendu comme valeur pour %s"
#, c-format
msgid "Expected %s, but found %s"
@@ -870,13 +870,13 @@
msgstr "Un nom de variable est attendu après un $."
msgid "Expected at least one argument"
-msgstr ""
+msgstr "Au moins un argument est attendu"
msgid "Expected exactly one function name"
-msgstr ""
+msgstr "Exactement un nom de fonction est attendu"
msgid "Expected exactly two names (current function name, and new function name)"
-msgstr ""
+msgstr "Exactement deux noms sont attendus (ancien et nouveau nom de fonction)"
msgid "Expected file path to read/write for -w:"
msgstr ""
@@ -921,11 +921,11 @@
#, c-format
msgid "Function '%s' already exists. Cannot create copy of '%s'"
-msgstr ""
+msgstr "La fonction « %s » existe déjà. Impossible de créer la copie de « %s »"
#, c-format
msgid "Function '%s' does not exist"
-msgstr ""
+msgstr "La fonction « %s » n’existe pas"
msgid "Generate random number"
msgstr "Génère un nombre aléatoire"
@@ -974,18 +974,18 @@
#, c-format
msgid "Illegal function name '%s'"
-msgstr ""
+msgstr "« %s » est un nom de fonction incorrect"
msgid "Illegal instruction"
msgstr "Instruction illégale"
#, c-format
msgid "Implicit int flag '%c' already defined"
-msgstr ""
+msgstr "Le sémaphore implicitement entier « %c » est déjà défini"
#, c-format
msgid "Implicit int short flag '%c' does not allow modifiers like '%c'"
-msgstr ""
+msgstr "Le sémaphore court implicitement entier « %c » n’autorise pas de modificateurs comme « %c »"
#, c-format
msgid "Incomplete escape sequence '%s'"
@@ -1006,15 +1006,15 @@
#, c-format
msgid "Invalid --max-args value '%s'"
-msgstr ""
+msgstr "La valeur « %s » de --max-args est invalide"
#, c-format
msgid "Invalid --min-args value '%s'"
-msgstr ""
+msgstr "La valeur « %s » de --min-args est invalide"
#, c-format
msgid "Invalid --unknown-arguments value '%s'"
-msgstr ""
+msgstr "La valeur « %s » de --unknown-arguments est invalide"
#, c-format
msgid "Invalid arg: %s"
@@ -1025,11 +1025,11 @@
#, c-format
msgid "Invalid count value '%s'"
-msgstr ""
+msgstr "La valeur « %s » de « count » est invalide"
#, c-format
msgid "Invalid end value '%s'"
-msgstr ""
+msgstr "La valeur « %s » de « end » est invalide"
#, c-format
msgid "Invalid escape sequence in pattern \"%s\""
@@ -1037,19 +1037,19 @@
#, c-format
msgid "Invalid escape style '%s'"
-msgstr ""
+msgstr "Le style d’échappement « %s » est invalide"
#, c-format
msgid "Invalid fields value '%s'"
-msgstr ""
+msgstr "La valeur « %s » de « fields » est invalide"
#, c-format
msgid "Invalid function name: %s"
-msgstr ""
+msgstr "Nom de fonction invalide : %s"
#, c-format
msgid "Invalid index starting at '%s'"
-msgstr ""
+msgstr "Indice invalide à partir de « %s »"
msgid "Invalid index value"
msgstr ""
@@ -1059,19 +1059,19 @@
#, c-format
msgid "Invalid job control mode '%s'"
-msgstr ""
+msgstr "Mode de contrôle de tâche « %s » invalide"
#, c-format
msgid "Invalid length value '%s'"
-msgstr ""
+msgstr "La valeur de taille « %s » est invalide"
#, c-format
msgid "Invalid level value '%s'"
-msgstr ""
+msgstr "La valeur de niveau « %s » est invalide"
#, c-format
msgid "Invalid limit '%s'"
-msgstr ""
+msgstr "La limite « %s » est invalide"
#, c-format
msgid "Invalid max matches value '%s'"
@@ -1087,7 +1087,7 @@
#, c-format
msgid "Invalid option spec '%s' at char '%c'"
-msgstr ""
+msgstr "Option invalide « %s » au caractère « %c »"
#, c-format
msgid "Invalid padding character of width zero '%s'"
@@ -1119,7 +1119,7 @@
#, c-format
msgid "Invalid start value '%s'"
-msgstr ""
+msgstr "Indice de départ « %s » invalide"
#, c-format
msgid "Invalid style value '%s'"
@@ -1127,7 +1127,7 @@
#, c-format
msgid "Invalid token '%s'"
-msgstr ""
+msgstr "Lexème invalide « %s »"
#, c-format
msgid "Invalid type '%s'"
@@ -1162,7 +1162,7 @@
msgstr ""
msgid "Key not specified"
-msgstr ""
+msgstr "Clé non spécifiée"
msgid "Language specifiers appear repeatedly:"
msgstr ""
@@ -1196,7 +1196,7 @@
msgstr ""
msgid "Missing -- separator"
-msgstr ""
+msgstr "Séparateur -- manquant"
msgid "Missing closing parenthesis"
msgstr ""
@@ -1216,7 +1216,7 @@
msgstr ""
msgid "Name cannot be empty"
-msgstr ""
+msgstr "le nom ne peut pas être vide"
msgid "Negate exit status of job"
msgstr "Inverser le code de retour de la tâche"
@@ -1229,18 +1229,18 @@
#, c-format
msgid "No abbreviation named %s with the specified command restrictions"
-msgstr ""
+msgstr "aucune abréviation nommée %s"
#, c-format
msgid "No binding found for key '%s'"
-msgstr ""
+msgstr "Aucun lien trouvé pour la combinaison %s"
#, c-format
msgid "No binding found for key sequence '%s'"
msgstr ""
msgid "No blocks defined"
-msgstr ""
+msgstr "Aucun bloc défini"
msgid "No catalogs available for language specifiers:"
msgstr ""
@@ -1251,7 +1251,7 @@
#, c-format
msgid "No suitable job: %d"
-msgstr ""
+msgstr "Aucune tâche appropriée : %d"
#, c-format
msgid "No suitable job: %s"
@@ -1312,7 +1312,7 @@
#, c-format
msgid "Permission denied: '%s'"
-msgstr ""
+msgstr "Permission refusée « %s »"
#, c-format
msgid "Please set $%s to a directory where you have write access."
@@ -1387,7 +1387,7 @@
#, c-format
msgid "Regular expression substitute error: %s"
-msgstr ""
+msgstr "Erreur de substitution de l’expression régulière : %s"
msgid "Remove job from job list"
msgstr "Supprimer la tâche de la liste des tâches"
@@ -1400,10 +1400,10 @@
msgstr "Redirection demandée à « %s », qui n’est pas un descripteur de fichier valide"
msgid "Requires at least two arguments"
-msgstr ""
+msgstr "doit être utilisée avec au moins deux arguments"
msgid "Requires exactly two arguments"
-msgstr ""
+msgstr "doit être utilisée avec exactement deux arguments"
msgid "Resource limit not available on this operating system"
msgstr ""
@@ -1471,7 +1471,7 @@
#, c-format
msgid "Short flag '%c' invalid, must be alphanum or '#'"
-msgstr ""
+msgstr "Le sémaphore « %c » est invalide : il doit être alphanumérique ou valoir « # »"
msgid "Show absolute path sans symlinks"
msgstr ""
@@ -1551,7 +1551,7 @@
#, c-format
msgid "The directory '%s' does not exist"
-msgstr ""
+msgstr "Le dossier « %s » n’existe pas"
#, c-format
msgid "The error was '%s'."
@@ -1615,7 +1615,7 @@
#, c-format
msgid "Tried to change the read-only variable '%s'"
-msgstr ""
+msgstr "Impossible de modifier la variable en lecture seule « %s »"
#, c-format
msgid "Tried to modify the special variable '%s' to an invalid value"
@@ -1668,7 +1668,7 @@
#, c-format
msgid "Unexpected argument -- '%s'"
-msgstr ""
+msgstr "argument inattendu -- « %s »"
msgid "Unexpected end of string, expecting ')'"
msgstr ""
@@ -1697,7 +1697,7 @@
#, c-format
msgid "Unknown color '%s'"
-msgstr ""
+msgstr "Couleur « %s » inconnue"
msgid "Unknown command"
msgstr ""
@@ -1719,7 +1719,7 @@
#, c-format
msgid "Unknown error trying to locate directory '%s'"
-msgstr ""
+msgstr "Erreur inconnue en tentant de localiser le dossier « %s »"
msgid "Unknown error while evaluating command substitution"
msgstr ""
@@ -1733,11 +1733,11 @@
#, c-format
msgid "Unknown input function '%s'"
-msgstr ""
+msgstr "Fonction d’entrée « %s » inconnue"
#, c-format
msgid "Unknown signal '%s'"
-msgstr ""
+msgstr "Signal « %s » inconnu"
msgid "Unmatched wildcard"
msgstr ""
@@ -1796,7 +1796,7 @@
msgstr ""
msgid "`set --show` does not allow slices with the var names"
-msgstr ""
+msgstr "« set --show » n’autorise pas de tranches avec les noms des variables"
msgid "a path variable"
msgstr "une variable de chemin"
@@ -1874,11 +1874,11 @@
#, c-format
msgid "exclusive flag '%s' is not valid"
-msgstr ""
+msgstr "le sémaphore exclusif « %s » est invalide"
#, c-format
msgid "exclusive flag string '%s' is not valid"
-msgstr ""
+msgstr "le sémaphore texte exclusif « %s » est invalide"
#, c-format
msgid "expected %d arguments; got %d"
@@ -1958,7 +1958,7 @@
#, c-format
msgid "job %d ('%s') was stopped and has been signalled to continue."
-msgstr ""
+msgstr "la tâche %d (« %s ») a été arrêtée et a reçu un signal pour continuer."
msgid "line/column index starts at 1"
msgstr ""
@@ -2016,7 +2016,7 @@
msgstr "arrêtée"
msgid "subcommand takes no options"
-msgstr ""
+msgstr "cette sous-command n’a pas d’option"
#, c-format
msgid "successfully set universal '%s'; but a global by that name shadows it"
@@ -2054,7 +2054,7 @@
#, c-format
msgid "unrecognized feature '%s'"
-msgstr ""
+msgstr "fonctionnalité non reconnue « %s »"
#, c-format
msgid "variable '%s' is read-only" |
|
And for "error rewrite: remove unused error from diff --git a/localization/po/fr.po b/localization/po/fr.po
index 14851fb19e..69b4e61a82 100644
--- a/localization/po/fr.po
+++ b/localization/po/fr.po
@@ -194,7 +194,7 @@
#, c-format
msgid "%s %s: options cannot be used together"
-msgstr ""
+msgstr "%s %s : ces options ne peuvent pas être utilisées ensembles"
#, c-format
msgid "%s (line %d)"
@@ -310,7 +310,7 @@
#, c-format
msgid "%s: invalid integer"
-msgstr ""
+msgstr "%s : entier invalide"
#, c-format
msgid "%s: invalid mode"
@@ -318,7 +318,7 @@
#, c-format
msgid "%s: invalid mode name. See `help %s`"
-msgstr ""
+msgstr "%s : nom de mode invalide. Voir « help %s »"
#, c-format
msgid "%s: invalid option argument: %s"
@@ -330,7 +330,7 @@
#, c-format
msgid "%s: invalid subcommand"
-msgstr ""
+msgstr "%s : sous-commande invalide"
#, c-format
msgid "%s: invalid variable name. See `help %s`"
@@ -342,11 +342,11 @@
#, c-format
msgid "%s: option does not take an argument"
-msgstr ""
+msgstr "%s : cette option ne prend pas d’argument"
#, c-format
msgid "%s: option requires an argument"
-msgstr ""
+msgstr "%s : cette option doit être utilisée avec un argument"
#, c-format
msgid "%s: unexpected positional argument"
@@ -354,7 +354,7 @@
#, c-format
msgid "%s: unknown option"
-msgstr ""
+msgstr "%s : option inconnue"
#, c-format
msgid "%s: value not completely converted (can't convert '%s')"
@@ -639,7 +639,7 @@
#, c-format
msgid "Could not find job '%d'"
-msgstr ""
+msgstr "Impossible de trouver la tâche « %d »"
msgid "Could not set terminal mode for new job"
msgstr "Impossible de paramétrer le mode du terminal pour la nouvelle tâche"
@@ -979,7 +979,7 @@
#, c-format
msgid "Invalid max value '%s'"
-msgstr ""
+msgstr "Valeur maximale « %s » invalide"
#, c-format
msgid "Invalid number: %s"
@@ -1283,7 +1283,7 @@
#, c-format
msgid "Regular expression compile error: %s"
-msgstr ""
+msgstr "Erreur de compilation de l’expression régulière: %s"
#, c-format
msgid "Regular expression substitute error: %s"
@@ -1471,7 +1471,7 @@
msgstr ""
msgid "There are no suitable jobs"
-msgstr ""
+msgstr "Aucune tâche appropriée"
msgid "There are still jobs active:"
msgstr "Des tâches sont toujours actives :"
@@ -1782,7 +1782,7 @@
#, c-format
msgid "expected %d arguments; got %d"
-msgstr ""
+msgstr "%d arguments attendus ; %d reçus"
#, c-format
msgid "expected <= %d arguments; got %d"
@@ -1843,7 +1843,7 @@
msgstr "précision invalide : %s"
msgid "invalid subcommand"
-msgstr ""
+msgstr "sous-commande invalide"
#, c-format
msgid "invalid underline style: %s" |
|
Updated script for french: Detailsimport polib
import copy
lang = ["de", "en", "es", "fr", "ja_JP", "pl", "pt_BR", "sv", "zh_CN", "zh_TW"]
prefixes = ["%s: ", "%s: %s: ", "%s %s: "]
zh_separator = ":"
fr_separator = " : "
for lang in lang:
pofile = polib.pofile(f"localization/po/{lang}.po.old")
# Add entries without prefix
for entry in pofile.translated_entries():
for prefix in prefixes:
if not entry.msgid.startswith(prefix):
continue
newid = entry.msgid.removeprefix(prefix)
if newid == "" or newid in prefixes:
continue
existing_entry = pofile.find(newid)
if lang.startswith("zh"):
msgstr_prefix = prefix.replace(": ", zh_separator)
elif lang.startswith("fr"):
msgstr_prefix = prefix.replace(": ", fr_separator)
else:
msgstr_prefix = prefix
if entry.msgstr.startswith(msgstr_prefix):
newstr = entry.msgstr.removeprefix(msgstr_prefix)
if existing_entry is None:
newentry = copy.copy(entry)
newentry.msgid = newid
newentry.msgstr = newstr
pofile.append(newentry)
elif existing_entry.translated():
if existing_entry.msgstr != newstr:
print(f"=== Bad match ({lang})\n- '{existing_entry.msgstr}'\n+ '{newstr}'")
pass
elif newid == newstr:
pass
else:
existing_entry.msgstr = newstr
# Add entries for removed prefixes
if lang.startswith("zh"):
for prefix in prefixes:
newid = f"{prefix}%s"
newstr = newid.replace(": ", zh_separator)
existing_entry = pofile.find(newid)
if existing_entry is None:
newentry = polib.POEntry(
msgid = newid,
msgstr = newstr
)
pofile.append(newentry)
elif existing_entry.translated():
if existing_entry.msgstr != newstr:
print(f"=== Bad match ({lang})\n- '{existing_entry.msgstr}'\n+ '{newstr}'")
pass
else:
existing_entry.msgstr = newstr
if lang.startswith("fr"):
for prefix in prefixes:
newid = f"{prefix}%s"
newstr = newid.replace(": ", fr_separator)
existing_entry = pofile.find(newid)
if existing_entry is None:
newentry = polib.POEntry(
msgid = newid,
msgstr = newstr
)
pofile.append(newentry)
elif existing_entry.translated():
if existing_entry.msgstr != newstr:
print(f"=== Bad match ({lang})\n- '{existing_entry.msgstr}'\n+ '{newstr}'")
pass
else:
existing_entry.msgstr = newstr
pofile.save(f"localization/po/{lang}.po") |
To homogenize error reporting format, use a new Error struct. Currently this is used for builtins and ensuring a common cmd/subcmd prefix.
Use the more generic `StringError::InvalidArgs` instead
panic!/unreachable! is immediately obvious (given that There was a missing French translation for the |
To homogenize error reporting format, use a new Error struct. Currently this is used for builtins and ensuring a common cmd/subcmd prefix. Part of #12556
Partial implementation of my idea for a more homogenized error printing.Currently, only
set_colorand the builtinsharedhelpers have been ported.All builtins and main have been updated.
Please comment.
The change in the localization is because I removed the "%s" for the command from the error message in favor of a
"%s{cmd}: %s{msg}", cmd, fmt!(msg, args)kind of thing (and similarly with a%s{cmd}: %s{subcmd}: %s{msg}for subcommands)I'm hesitating to add
with_option(wstr)+with_options(wstr,wstr). The main concern here is either a combinatorial explosion inprint_msg(for a total of 9 branches), or the deep-ish nesting ofwgettext_fmt!calls (cmd/subcmd > options > msg > who knows how many to construct msg)TODOs:
Fixes issue #<issue-number>