Skip to content

Instantly share code, notes, and snippets.

@morficus
Last active August 29, 2015 14:20
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save morficus/e2fabf24b44ff94978a7 to your computer and use it in GitHub Desktop.
Save morficus/e2fabf24b44ff94978a7 to your computer and use it in GitHub Desktop.
How to run (and what to look for in) the Ghost i18n proof-of-concept as well as my thoughts around a proposed workflow

These instructions are intended help anyone who is interested in demo'ing the Ghost i18n proof-of-concept (or PoC) that I am throwing together. This PoC is for both the admin panel as well as server-side notifications.

(PS: this is all pre the new Zelda UI changes)

See the POC in action

  1. Clone Ghost from my fork on GitHub
  2. Switch over to the i18n-poc branch (git checkout i18n-poc)
  3. Run npm install to download all node-related dependencies.
  4. Now follow the regular Ghost "developer install from git" instructions. https://github.com/TryGhost/Ghost#developer-install-from-git
  5. Once you are done with the those instructions, grunt dev` to start Ghost in development mode. Ghost will then be running at http://localhost:2368/ghost/
  6. Because this is just a proof of concept, the language files are inlcuded as part of the repo (in the real world, this would not be the case)
  7. Also because this is just a PoC... only a small subset of items have been translated:
  • Main navigation in the admin panel
  • Settings --> About page (also in the admin panel)
  • Initial notifications when you first run the blog (the "Welcome to Ghost" and "Trying to send an email" ones)
  • The "Welcome to Ghost" email
  1. To force another locale, open /core/client/app/app.js and change the array on line 18n to only have a single locale of your choice (currently the only ones avilable are en and es)

How does it work

Client-side

As you probablly already guessed, /core/client/app/app.js is where the locale is being set. This is thanks to the ember-intl library (which is part of formatjs

Server-side

In nodeJS-land I'm using intl-messageformat

Files you may care about:

If you want to see all files that were changed then take a look at this commit for al lthe client-side stuff and this commit for all the server-side stuff

Questions

My attempt to shed some insight in to my thought process and anticipate what an outsider looking in might wonder.

Why do the translation files have a nested structure?

The intent was to try and keep things manageble and organized. So the JSON structure follows the page nagivation structure (root-level elements are the same as what is in the nav, 2nd level elements are sub-nav, etc).
If you have any type of development background this might seem silly because the first thing you would want to do is try and optimize things in a way that the strings are reusable across pages. That works great for code but terrible for tranlations because context can change a lot. Just because 2 strigs (sentences) are the same in English, that does not mean that they will be the same in German. So following the navigation structure was the best way (I could come up with) to maintain context.

How did things get in to Transifex?

I manually created the source language file (en.json) and uploade it via their web interface.
The translations were done via their web editor.

What tools/libs are being used?

  1. grunt-transifex-keyvaluejson, to download the translated strings in to the Ember app.
  2. ember-intl, 2.0 preview, (which is part of the formatjs family) for the string replaments and locale setting (the 2.0 branch is not production ready, but it is stable enough for the POC. See ember-intl/issues/88 for a list of what to expect in 2.0 or the 2.0 PR for implementation details)
  3. Transifex, for managing the translations. Why Transifex over the others? Their API was the nicest and their web editor was awesome.

Workflows and oganizational structure

Here are my ideas around what the workflow would look like by role.
This is a non-technical bit of implementing i18n. (take a look at this and this to get a familair with the Transifex terms)

Structure

Workflow

Development

  • The translation strings should be commited to the repo and the grunt i18n should only be used to update the translation files when necesary (ideally by the "Organization Administrators" or "Project Maintainers").
  • By committing the translation files, the i18n process is (mostly) transparent and is not disruptive to the other developers.
  • The source file (en.json) is manually updated with any new strings and then uploaded to Transifex (maybe just the web interface?maybe through a plugin? we will cross that bridge when we get there) commited to repository and merged in just like nay other pull request. The updated en.json file will automatically make its way to Transifex via this functionality

Production release

  • At some point, the Transifex project needs to be marked as "not accepting transaltions" in order to properly test before a release.
  • All transaltion files are part of the package (short-term plan onle).

Open Questions

  1. What happens when somebody needs to introduce a new string while working on a particular feature? How does this get added to the source file and in to Transifex with out holding up development or waiting for an admin or maintainer to be avilable?
  2. What does testing look like to ensure that the design does not break when things are translated?

Roles & Responsabilities

Organization Administrators

Keepers of the realm. Most likely only Hannah and/or John They will also be responsable for identifying "project maintainers" and "language coordinators".

Project Maintainers

Keepers of the source file. They will be responsable for making sure that strings around the application are "harvested" and that the proper resource is updated in Transifex. They will also be responsable for identifying "language coordinators" and posting annoucments regarding progress and what not.
They will also be the ones to ensure that once a particular resourece reaches an acceptable maturity, that the translation file is commited to the Ghost repo.

Language Coordinators

Responsable for keeping a particular language on-track by identifying tranlators and up-to-par by reviewing trnaslations, seeking out reviewers and marking strings as "reviewed".

Translators

As the name implies, the ones doing the actual translation.
The expectation is that everyone with higher roles will also activley participate in translating strings.

@ErisDS
Copy link

ErisDS commented May 5, 2015

Off the top of my head (without trying the code yet + I can't find en.json) here are two bits of feedback:

So following the navigation structure was the best way (I could come up with) to maintain context.

This will only make sense for as long as the navigation stays the same and I doubt that will be true in 3 months time. Whilst context-based sections/hierarchy makes sense, I think this likely needs to be broken down into context based sections which aren't tied closely to the navigation.

With regard to development cycles & releases

At some point, the Transifex project needs to be marked as "not accepting transaltions" in order to properly test before a release.

What happens when somebody needs to introduce a new string while working on a particular feature? How does this get added to the source file and in to Transifex with out holding up development or waiting for an admin or maintainer to be avilable?

I think new strings should be submitted to Transifex automatically when a pull request is merged. Only the core team can merge PRs, so this is implicit permission to add new strings to transifex.

After that it will be up to the people translating Ghost to submit translations for the new features quickly. The latest translations should be pulled from transifex as part of the release process and missing translations would not hold up a release. The admin UI needs to be (and is always) designed with flexible string length in mind and those people managing/accepting translations should be mindful of where in the UI a string lives when accepting. In the case that the UI breaks for a particular language because of a long string, that will be treated as either a translation or design bug (depending on the case) and fixed in the next release.

One thing worth noting, is we only expect the standard admin UI to support LTR languages. Other writing systems will likely need far more than just translations, and that's where the concept of a language pack (with design modifications) comes in. However, that's definitely not something for this first iteration of i18n support.

@morficus
Copy link
Author

morficus commented May 6, 2015

I can't find en.json

It's actually not in the repo yet (tho it really should be...I'll add that today). Once you run grunt i18n it will pull it down.
After thinking of this last night, it would make sense for en.json to always be in the repo (because that will be the source language) and then all the other translated string files will only be pulled down from Transifex at release time (or if a user wants to do it manually) but never committed to the repo.

Whilst context-based sections/hierarchy makes sense, I think this likely needs to be broken down into context based sections which aren't tied closely to the navigation.

Makes sense, especially if things are expected to move around.

I think new strings should be submitted to Transifex automatically when a pull request is merged.

Automatically updating things in Transifex after a PR would be really awesome. Will have to do some research to figure out how to achieve this.

@SiR-DanieL
Copy link

In the case that the UI breaks for a particular language because of a long string, that will be treated as either a translation or design bug (depending on the case) and fixed in the next release.

I think that the translation should be indipendent. If there's a bug in a translation we should be able to update it without updating Ghost too.

It might happen very frequently, due to wrong quotes and/or other signs, for example a string like Carica l'immagine in italian could break the JavaScript and block Ghost completely. In this case it's better to update the translated JSON and ask people to update their language packs instead of waiting for the next update of Ghost.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment