Internationalization
joshwcomeau opened this issue · 17 comments
Guppy's goal is to remove barriers for beginners to learn how to do modern web development. One huge barrier is that tools like create-react-app are only available in English, and many aspiring web developers don't speak English!
It would be awesome to get i18n support in Guppy. Unfortunately, it's also a huge amount of effort, and not one I foresee having time to develop. If folks are interested in contributing, the first step is to add i18n support to the application. One upside to building a desktop app is that small sizes aren't so critical, so we could probably get away with a pretty minimal solution:
- Create files (JSON? YAML? JS objects?) that hold all the copy in English, at
/copy/en.js
or similar - Create a way to change languages in the UI (maybe a flag in the top-right, when no project is selected?) - Still open & not decided yet
- Add an application menu option for languages
- Use https://github.com/yahoo/react-intl or similar to format messages in different languages
- Update all the english copy
- Request translations, update when translations are available.
Current status (WIP see branch feature-i18n
)
- Create localization guide for easier getting started with the translations
- Planned / active localizations
- English locale (en) @zianke / @llllish
- Chinese locale (zh) @zianke / @llllish
- German (de) @AWolf81 (not started yet - waiting for English locale)
- Spanish (es) @Canopix (also waiting)
- Bulgarin (bg) @scottwarren (waiting for English locale - if still interested)
- Turkish (tr) @bennygenel (waiting for English locale)
Hey @joshwcomeau , I'd love to help out with this project and this seems like a great starter issue.
One question - in terms of design, would you prefer a flag in the top-right when no project is available or perhaps a more permanently accessible feature? I was thinking we could introduce a 'settings' view (accessed by a settings cog/icon on the bottom of the left-hand navigation pane?) and have language options within there? I feel that as this project evolves, users will have a need for a number of settings so it could be a good idea to create a dedicated space for this early on?
That's just my 2 cents though, would love to hear your thoughts!
It would make more sense to detect the user's preferred languages, I think those should be in navigator.languages
or navigator.language
Would it not be sufficient to use the language and locale set in the operating system? I don't commonly see native apps have their own language selector.
React-intl/FormatJS seems like a solid solution, and it's good to start these things early on when there aren't too many hard coded strings all over the place.
Since this application is going to be geared towards lightly technical users, who might have great enthusiasm to help, but not necessarily the confidence yet to contribute via GitHub, I think it would be good to start the language files from the beginning in such a way that they can be used with a translation service like transifex later on. Not a big issue though, since e.g. transifex supports plain json with key:message pairs using the ICU rules also used with FormatJS.
I can extract the strings from the app and do the german translations. I can also look into detecting the system language and locale and setting up react-intl (though thats probably quite a large task).
As a user of a "en_DE" locale I am also a nice edge case myself.
Thanks for the feedback y'all! Excited to see so much enthusiasm around this issue :D
Yeah, I should really check how other apps do this. Using the OS language seems like the best idea (the best UI is no UI!), with maybe an override in the application menu if it's deemed necessary.
I'm unfamiliar with transifex, but agree a service might be nice to encourage translations from folks not familiar with Git!
As a user of a "en_DE" locale I am also a nice edge case myself.
Awesome 👍
I would love to help out too. I'm on a Windows machine so I can test/integrate OS language detection and also I'm "tr_TR" local. I can help to translate strings to Turkish.
I have worked with i18next before a little bit. It has a library for language detection for electron (i18next-electron-language-detector) which can be helpful. I think its a good library to consider.
@joshwcomeau If we plan on supporting multiple languages in our documentation, I think we should add some way to add the different languages within our repo (instead of having users host their own versions). That way we have ultimate control over the structure/consistency. Maybe a docs repo is best suited? WDYT?
I'm en_AU
but I also can help with translation to Bulgarian/Български 🇧🇬 (FWIW/note: I am far from fluent though)
If we plan on supporting multiple languages in our documentation, I think we should add some way to add the different languages within our repo (instead of having users host their own versions). That way we have ultimate control over the structure/consistency. Maybe a docs repo is best suited? WDYT?
Yeah, so I agree with this in theory :) I think the reason the Chinese version is internationally hosted is because it was the easiest way to support it.
A more polished solution probably would be to have a guppy-docs
repo that has a directory for each region/lang.
That said, I'm not sure the project is mature enough for it to be worth the hassle... Once we have multiple languages, we'll need people committed to keeping those languages up to date, so we'd probably need to have some sort of external tool. Once the english docs are updated, we'd need to broadcast the fact that other languages need to be updated as well, have people be able to "claim" a language update...
Working with i18n at work (Khan Academy is available in dozens of languages) shows how much administrative overhead there can be.
So yeah, I totally agree that this could be much better, but wonder if maybe we should wait until the product is in a state of less churn to get it done?
(also, for in-product i18n... I feel like this is slightly more manageable, since it works on a per-string basis. The worst case is occasional english slips through, which is far from ideal but also means that the overall product is still workable, since folks can just google the strings they don't understand... with outdated docs, it feels much more dangerous/frustrating)
So yeah, I totally agree that this could be much better, but wonder if maybe we should wait until the product is in a state of less churn to get it done?
re: all of your points are very accurate, it is a lot of overhead, and quite possibly too early in the project's life for this kind of thing. Maybe we can leave this open for the future, but not worry too much about it now?
EDIT: To go further, especially considering the age of the project, it might be "biting off more than we can chew" in regards to trying to do all the things. It could be just a "help wanted" if someone wants to pick it up, like you suggested originally in this issue.
(also, for in-product i18n... I feel like this is slightly more manageable, since it works on a per-string basis. The worst case is occasional english slips through, which is far from ideal but also means that the overall product is still workable, since folks can just google the strings they don't understand... with outdated docs, it feels much more dangerous/frustrating)
+1 to this, having an updated English version/string is infinitely more useful than an outdated version in their language.
Hi all, the issue looks very interesting to me. I have some experience with i18n and have used react-intl in my own projects. Also it would be great for me to take this as one of my first open source contributions. If no one else is currently working on it, I'm planning to make a fork and try to implement it these days. It might take a period of time considering the project size and the change of code structure.
I'll try to add react-intl to the UI code and copy all the English strings to a JSON. The suggestion of taking the OS language sounds like a good starting point, although someone might want to add a language selection feature later. Besides, as a native Chinese speaker, I'd be happy to provide a Chinese translation if time permits.
I have a friend to collaborate with me on these updates. Welcome to join us if you are also interested!
hi @zianke, sounds great.
Before you start, could you please compare react-intl
with react-i18next? I would use
react-i18next` as it seems to be easier to start. I have created a codesandbox for it.
I like the code just the hooks should be changed to class-based code as we're not yet using hooks in Guppy.
Would be awesome to see the same for react-intl
in a sandbox but it wasn't working. Maybe I'll try it again later.
react-intl
would be also OK, I just had issues to get it up an running.
To the structure of the translations:
What is the best way to group the translations? Should we make a namespace for each group e.g. projectPage
could be one for ProjectPage
component or do we need to do it more fine-grained?
Loading of the locale
I think for Guppy it's OK to bundle it with the app - so no need to use XHR like in the Codesandbox above. We can use imports to load the locale.
Hi @AWolf81 , thanks for your demo. We've also created a simple codesandbox demo for react-intl
. You might find it helpful.
Although react-i18next
is also a great choice, I personally would prefer to use react-intl
in the updates because I'm more familiar with it. We can easily group all the translations with react-intl
, as you can see in the demo's src/translate
directory. For this project, I think we can create an id for each string in the format "guppy.projectPage.stringName"
.
Cool, thanks. Yes, the demo helped and I think it's OK to use if you prefer it.
Just some points I think would be good to have:
- Use
messageDescriptor
with flow typing - Add message descriptor to same folder as component e.g
app.messages.js
- Rename
translate
folder tolocale
and add subfolders for each language & add a json file for every component translation. Or is this not needed? What do you think? I think adding everything into one file is probably bad as there are components with a lot of text and it's more difficult to find the translation in a large file.
Please have a look in this codesandbox and for flow typing the following post will help.
For the automatic language picking we could use window.navigator.language
. (Not tested but it should work in an Electron app) We can also change the default and store it in localstorage. A good place for the selected language would be app-settings.reducer as general.currentLanguage
.
The UI language switcher will be in app settings modal.
I would also add a language switching menu item to ApplicationMenu so it's easier to switch between languages during development.
I would start with this as it's easier to add and the Settings modal is OK to add afterwards.
If you need any help please let me know. If you like, I could add the application menu item & the app-settings.reducer
stuff.
Thanks, @AWolf81 . The message descriptor works.
For the file structure of locale
directory, I think it depends on how many messages and how many components with messages we have. If there are a lot of them, then putting messages of each component in a separate JSON makes it more convenient to find one. But this can also lead to a larger number of import
s when creating the IntlProvider
. So my plan for next days would be to make the app.messages.js
first, by listing all the messages to be replaced in each component, maybe also recording the dates, numbers, etc. to be formatted. After the app.messages.js
is finished, we can see the total numbers and decide if we need a separate JSON for each component.
I agree that we'll need a language switcher before enabling i18n in the real UI. Otherwise Chinese users, for example, might find it troublesome if they are not able to switch back to English. But for now, we can test react-intl
by using a hard-coded default language first, just like the one in codesandbox. After that, if you've made the app settings modal or application menu and the reducer ready, that would be perfect. I'd also be happy to offer any help in integrating these parts together.
@zianke, yes, you're right and a good idea to see how many messages we have and if it is getting a large file we can refactor/split to multiple files.
OK, I've started the app menu and reducer - it is ready and you can merge that into your branch to replace the hard coded value.
You can find the menu item & reducer in branch i18n-app-menu-item. I think it's already usable - just the position of the menu item could change. At the moment, it is in File/Language
. I've asked Melanie in Gitter if there is a better location for this.
Also two constants may change it's location languageCaptions
and languageCode
from ApplicationMenu
- not sure what's the right location for them.
The state you need for your localization is in appSettings.general.language
and you can use getLanguage
from app-settings.reducer
in mapStateToProps
to get language
into your connected component. You can also see ApplicationMenu
component in the mentioned branch to see the usage.
@zianke how is the status of this? Have you made progress?
If I can support you with anything please let me know.
Hi @AWolf81 , you can see our current progress here.
We (I and @llllish) have listed all the message descriptors (excluding those hardcoded strings in non-component javascript code, such as types.js
. We may need to discuss how to solve this later. ) Currently we are working on the Chinese translation. Hopefully it can be finished today or tomorrow.
We've also created an intl demo on the IntroScreen, so you can try to open this screen and switch the language to Chinese. Our approach is adding a intl.reducer.js
to store locale and messages, as well as updating index.js
with the IntlProvider
of react-intl-redux
, which requires two keys intl.locale
and intl.messages
in redux state. We are not sure if this is the best approach, but so far it works fine on the IntroScreen, and it can avoid too much change on the redux structure.