-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement localization using gettext files — I18N — L10N #2090
base: main
Are you sure you want to change the base?
Conversation
This PR exceeds the recommended size of 200 lines. Please make sure you are NOT addressing multiple issues with one PR. Note this PR might be rejected due to its size. |
caedfc5
to
fb482c2
Compare
This PR exceeds the recommended size of 200 lines. Please make sure you are NOT addressing multiple issues with one PR. Note this PR might be rejected due to its size. |
- Recipe to extract new translations from the Go code: `make i18n_extract` - Embedded `MO` files - Detect language from environment variables - Some strings were pluralized
fb482c2
to
ab14efa
Compare
This PR exceeds the recommended size of 200 lines. Please make sure you are NOT addressing multiple issues with one PR. Note this PR might be rejected due to its size. |
I know it's a lot of added lines, but more than half are from the |
Thanks @Goutte! A couple of things to help get this reviewed: |
@Goutte could you explain what the process would be, if we adopted this PR, for a cobra contributor to add a new string printout? It is not clear to me when At this point, clarifying these steps in the PR description is sufficient, but if the review goes well, we'll need those details in some cobra documentation. Also, I'm also interested in understanding how a project using Cobra would proceed to add translations, or is that completely up to the project and this PR does not help? |
I see this already in the docs. Nice work. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just started looking at this but I'm staring to get it.
Seems good at first impression.
But, I feel we absolutely need a way to generate the *.mo
files from the command-line, through the makefile in fact.
Makefile
Outdated
@@ -33,3 +33,7 @@ install_deps: | |||
|
|||
clean: | |||
rm -rf $(BIN) | |||
|
|||
i18n_extract: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do I understand correctly that whenever a file changes, this make target must be re-run to update the line numbers in the default.pot
file?
If that is the case, then it is essential that we run this make target in .github/test.yml
and check if default.pot
(or anything else) gets changed. I don't expect many contributors to know they have to run this, so CI should fail if they don't
@@ -33,15 +34,15 @@ func legacyArgs(cmd *Command, args []string) error { | |||
|
|||
// root command with subcommands, do subcommand checking. | |||
if !cmd.HasParent() && len(args) > 0 { | |||
return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.findSuggestions(args[0])) | |||
return fmt.Errorf(gotext.Get("LegacyArgsValidationError"), args[0], cmd.CommandPath(), cmd.findSuggestions(args[0])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about leaving the original english string as the index for all the gotext.Get()
calls?
It would make reading the code much easier and it would make working with the *.po
files easier as the original text would be right there as the index.
If the %
formatting gives a problem, maybe we can replace it with %%
in the index? It is not ideal, but it would be manageable. So, for this line here we would instead use (unless there is a better way?)
return fmt.Errorf(gotext.Get("unknown command %%q for %%q%%s"), args[0], cmd.CommandPath(), cmd.findSuggestions(args[0]))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’ve looked at gotext in more detail, and the %
form will work just fine as long as the parameters are in the gotext.Get()
call instead of outside.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the crux of the matter.
I've done a whole bunch of implementations of translations in the past, and every time I try to use plain english instead of keys I end up either regretting it or refactoring heavily to keys.
Here's some food for thought when using raw english as keys:
- cosmetically changing the english string (typo, whitespace, etc.) invalidates all the existing translations (possibly hundreds of files need to be updated, for nothing)
- compels usage of
context
shenanigans for strings that are the same in english in different contexts but different in other languages - too many gettext parsers out there choke on nasty translation ids like
the "quoted q'tara" <tag>
- sometimes it creates a bias towards english in the structure of strings, that need to be fixed by translators, straight in the code, once again invalidating all existing translations
I'm very aware that using keys makes the code harder to read and understand. This is mitigated a little by a careful choice of the wording of the key.
I had to pick one way or the other ; it was not an easy choice, but it's one I made many times and I decided to go with hindsight from past experiences.
I'm not adamant on this, quite the contrary.
I listed some of the key points of my decision above (probably forgot some) ; I'll let y'all be the final judges. Good luck !
args.go
Outdated
@@ -16,6 +16,7 @@ package cobra | |||
|
|||
import ( | |||
"fmt" | |||
"github.com/leonelquinteros/gotext" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could import this as i18n
, so that instead of calling gotext.Get()
we would call i18n.Get()
.
This may be clearer when browsing the code?
Something to discuss later, I just don’t want to forget. We will need a way for a program using cobra to disable these translations. For example, if a So we probably want the project to explicitly have to turn on translations through a global setting. |
Thanks for the comprehensive review, @marckhouzam !
This PR does not help devs to implement their I18N. We could perhaps provide some doc/sugar on how to use the same translation toolkit as cobra, since the relevant libraries are already included ?
Good question. Since gettext is mature, I'm pretty confident there are CLI tools out there handling this. (or we'll have to make one, preferably in Golang, using cobra of course) I'd like the MO to be generated by CI as well, if only to prevent contributors to inject nasty things in the binary files. I'll look into this.
Very good point. Partially translated apps are awkward, which is precisely why I wanted cobra to support i18n. I'm not sure right now how to proceed to add a global setting to cobra, but I'll look into it and come back to ask for help if I don't find how. Do give tips if you'd prefer it to be done in some specific way :) |
To allow a project to enable translations I’m thinking of two approaches you can take:
if you think different settings might be useful eventually, I would recommend using a struct so we could grow it overtime |
Thanks for clarifying, it makes sense. Let’s leave this for a potential separate PR and focus on cobra’s strings . |
I think cobra should have a built tag to disable/enable translations. Right now it looks like all translations are embedded in the final binary. That will lead to unnecessary bloat for users who do not want to use it so a build tag would be the better option IMO. And these translations should be opt in not opt out IMO. |
Good idea +1
Agreed 👍 |
This PR exceeds the recommended size of 200 lines. Please make sure you are NOT addressing multiple issues with one PR. Note this PR might be rejected due to its size. |
This PR exceeds the recommended size of 200 lines. Please make sure you are NOT addressing multiple issues with one PR. Note this PR might be rejected due to its size. |
That's a good idea. I think I managed to do this using the "locales" build tag. Now I need to tweak the test suite to use that build tag, Anyhow, for now the tests are broken until I figure this out. |
This PR exceeds the recommended size of 200 lines. Please make sure you are NOT addressing multiple issues with one PR. Note this PR might be rejected due to its size. |
See #1134
Features
make i18n_extract
gettext
formatMO
filesIssues
No regional fallback for now, waiting for leonelquinteros/gotext#85 to be merged.
This will also unlock fallback to english when a translation is not found.
Replaces #1944