{"action":"created","issue":{"url":"https://api.github.com/repos/TryGhost/Ghost/issues/5345","labels_url":"https://api.github.com/repos/TryGhost/Ghost/issues/5345/labels{/name}","comments_url":"https://api.github.com/repos/TryGhost/Ghost/issues/5345/comments","events_url":"https://api.github.com/repos/TryGhost/Ghost/issues/5345/events","html_url":"https://github.com/TryGhost/Ghost/issues/5345","id":81032378,"number":5345,"title":"i18n, proposed tooling and workflow","user":{"login":"morficus","id":718799,"avatar_url":"https://avatars.githubusercontent.com/u/718799?v=3","gravatar_id":"","url":"https://api.github.com/users/morficus","html_url":"https://github.com/morficus","followers_url":"https://api.github.com/users/morficus/followers","following_url":"https://api.github.com/users/morficus/following{/other_user}","gists_url":"https://api.github.com/users/morficus/gists{/gist_id}","starred_url":"https://api.github.com/users/morficus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/morficus/subscriptions","organizations_url":"https://api.github.com/users/morficus/orgs","repos_url":"https://api.github.com/users/morficus/repos","events_url":"https://api.github.com/users/morficus/events{/privacy}","received_events_url":"https://api.github.com/users/morficus/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/TryGhost/Ghost/labels/help%20wanted","name":"help wanted","color":"009800"},{"url":"https://api.github.com/repos/TryGhost/Ghost/labels/i18n","name":"i18n","color":"94D3DE"}],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/TryGhost/Ghost/milestones/22","html_url":"https://github.com/TryGhost/Ghost/milestones/Current%20Backlog","labels_url":"https://api.github.com/repos/TryGhost/Ghost/milestones/22/labels","id":834413,"number":22,"title":"Current Backlog","description":"High priority issues that we would like to complete within the next few weeks","creator":{"login":"ErisDS","id":101513,"avatar_url":"https://avatars.githubusercontent.com/u/101513?v=3","gravatar_id":"","url":"https://api.github.com/users/ErisDS","html_url":"https://github.com/ErisDS","followers_url":"https://api.github.com/users/ErisDS/followers","following_url":"https://api.github.com/users/ErisDS/following{/other_user}","gists_url":"https://api.github.com/users/ErisDS/gists{/gist_id}","starred_url":"https://api.github.com/users/ErisDS/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ErisDS/subscriptions","organizations_url":"https://api.github.com/users/ErisDS/orgs","repos_url":"https://api.github.com/users/ErisDS/repos","events_url":"https://api.github.com/users/ErisDS/events{/privacy}","received_events_url":"https://api.github.com/users/ErisDS/received_events","type":"User","site_admin":false},"open_issues":17,"closed_issues":61,"state":"open","created_at":"2014-10-21T13:40:12Z","updated_at":"2015-07-06T20:03:18Z","due_on":"2015-07-13T23:00:00Z","closed_at":null},"comments":5,"created_at":"2015-05-26T16:16:19Z","updated_at":"2015-07-07T08:32:36Z","closed_at":null,"body":"This issue is intended to act as a catalyst for getting the Internationalization ball rolling, discussions going and decisions made.  \r\nBelow are a few things I'm proposing is based on a mix of my own research and previous conversations in the #i18n Slack channel.  \r\nI obviously do not have all the answers (and there is a chance that the ones I have could be improved on) so please fill in what ever gaps you see or propose changes where ever you see fit.  \r\n\r\n\r\n\r\n## Glossary of terms\r\n\r\n* i18n: internationalization \r\n* BLOS: big list of strings\r\n* admin: the admin section that lives under the /ghost url\r\n* frontend: non-admin panel section, where articles are published and publicly visible\r\n\r\n\t\r\n## Proposed Tooling\r\n\r\n#### [Transifex](http://transifex.com/), for managing translations:\r\n - Transifex has a pretty comprehensive API (http://docs.transifex.com/api/) for interacting with their resources as well as a few grunt plugins in case we need to update the dev workflow (like [this](https://www.npmjs.com/package/grunt-transifex-keyvaluejson), [this](https://www.npmjs.com/package/grunt-transifex), and [this](https://www.npmjs.com/package/grunt-transifex-resjson))\r\n - It also has a mechanism for keeping the source language file in-sync with a file on GitHub , which would make it really easy to keep things updated ([full details](http://docs.transifex.com/faq/#can-i-update-source-files-automatically)).\r\n\r\n > As long as your source file is accessible from a public URL, such as this raw file on GitHub, Transifex will periodically check the URL for updates. You can also use a file from a private repo in Github as long as the URL has the respective token that Github provides. If there are updates in the file, Transifex automatically fetches the file and updates your content.\r\n\r\n#### [ember-intl](http://formatjs.io/ember/), for client/Ember translations\r\n - Part of the [Format.js](http://formatjs.io/) library collection\r\n - I compared it to [ember-i18n](https://github.com/jamesarosen/ember-i18n) and [ember-cli-i18n](https://github.com/dockyard/ember-cli-i18n), Format.js provides a more complete feature set\r\n - It does not officially support sideloading BLOS in the current, but it is implemented in the 2.0 branch ([see issue ref](https://github.com/yahoo/ember-intl/issues/88))\r\n - It goes beyond just strings, but also includes template helpers for formatting dates, numbers and time per locale \r\n - Includes template helpers for providing custom formatting for dates, numbers and time\r\n - Follows well-known [ICU message syntax](http://userguide.icu-project.org/formatparse/messages) for pluralization, select and variable interpolation\r\n - Leverages the standards-compliant window.Intl object and [provides a polyfill](http://formatjs.io/guides/runtime-environments/#client) for browsers that do not have support for it as yet.\r\n\r\n#### [intl-messageformat](https://github.com/yahoo/intl-messageformat), for server-side translations\r\n - I also looked at [messageformat.js](https://github.com/SlexAxton/messageformat.js) and [i18next-node](https://github.com/i18next/i18next-node) but they seemed for fall short in some aspects.\r\n - Also part of the [Format.js](http://formatjs.io/) library collection\r\n - Biggest sticking point is that it follows [ICU message formats](http://userguide.icu-project.org/formatparse/messages) and[ Unicode CLDR standards](http://cldr.unicode.org/)\r\n - Leverages native ECMAScript i18n API functionality for date and currency formatting ([polyfill if no available in engine](http://formatjs.io/guides/runtime-environments/#server))\r\n - Usage is kind of clunky, but that can be hidden behind a facade for a better developer experience\r\n\r\n## Proposed Rollout\r\nWe should be doing a staged-rollout of the i18n functionality. The ideal end-state is for Ghost to have the concept of \"language packs\" that the user can install on demand when ever they want. But this long-term vision is some-what dependent on (or at they ver least related to) Ghost Apps being flushed out. The phases detailed out below are intended to lead up to this ideal state.\r\n\r\n#### Phase 1 - harvesting strings\r\nRegardless of what process or workflow we end up following to distribute translations, all the hard coded strings (both in the Ember and Node app) will need to be moved in to a \"source language file\". So phase 1 would be to do a basic implementation of the i18n libs mentioned above. [Ember sample](https://github.com/morficus/Ghost/blob/i18n-poc/core/client/app/templates/-navbar.hbs#L4), [Node sample](https://github.com/morficus/Ghost/blob/i18n-poc/core/server/index.js#L29)  \r\nAt the end of phase 1 English would still be the only language supported in Ghost and there would be no noticeable change to the end-user.\r\n\r\n#### Phase 2 - start translating strings\r\n(this can be done in parallel with Phase 1 once there is a decent amount of strings are in Transifex)  \r\nOnce we have our base \"source language file\" (most likely `en.json`), the next step would be to setup Transifex and let the translators commence the translating in to the initial target languages.  \r\nThis process will be on-going as the \"source langue file\" will continue to be updated and new languages may be introduced as well.\r\n\r\n#### Phase 3 - develop locale-setting functionality\r\nBoth the Node and Ember app need enhancements to (a) ask the user for their locale during setup, (b) modify locale in \"settings\" and (c) allow for the locale value to be programmatically retrieved. This might go through a few iterations depending on how we want to allow users to control locales (blog-wide? per-user basis? only during setup?). At this point, i18n could be enabled under Labs with limited locale support (depending on progress of phase 2).  \r\nLocale files will still need to be bundled with the release (not a big deal YET, because there would be 2 or 3 locales at most)\r\n\r\n\r\n## Proposed Development Process / Workflow\r\nThe source language file (`en.json` for both client and server) is the only language file that will be in the repo. This file will be kept in-sync with Transfex using [this process](http://docs.transifex.com/faq/#can-i-update-source-files-automatically). All other language files will be downloaded as needed.  \r\nLong-term, the only language file that should be in the downloadable `ghost-*.zip` file should also be `en.json`. But short-term, until apps are ready, any other supported locale will need to be included as well (hopefully this will only be necessary for 1 or 2 locales).\r\n\r\n## Proposed Workflows and organizational structure\r\nSee the [\"Workflows and organizational structure\" section in this gist](https://gist.github.com/morficus/e2fabf24b44ff94978a7#file-4-workflows-md) for information related to this topic."},"comment":{"url":"https://api.github.com/repos/TryGhost/Ghost/issues/comments/119119986","html_url":"https://github.com/TryGhost/Ghost/issues/5345#issuecomment-119119986","issue_url":"https://api.github.com/repos/TryGhost/Ghost/issues/5345","id":119119986,"user":{"login":"JohnONolan","id":120485,"avatar_url":"https://avatars.githubusercontent.com/u/120485?v=3","gravatar_id":"","url":"https://api.github.com/users/JohnONolan","html_url":"https://github.com/JohnONolan","followers_url":"https://api.github.com/users/JohnONolan/followers","following_url":"https://api.github.com/users/JohnONolan/following{/other_user}","gists_url":"https://api.github.com/users/JohnONolan/gists{/gist_id}","starred_url":"https://api.github.com/users/JohnONolan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JohnONolan/subscriptions","organizations_url":"https://api.github.com/users/JohnONolan/orgs","repos_url":"https://api.github.com/users/JohnONolan/repos","events_url":"https://api.github.com/users/JohnONolan/events{/privacy}","received_events_url":"https://api.github.com/users/JohnONolan/received_events","type":"User","site_admin":false},"created_at":"2015-07-07T08:32:36Z","updated_at":"2015-07-07T08:32:36Z","body":"Some very relevant reference material: http://responsivenews.co.uk/post/123104512468/13-tips-for-making-responsive-web-design"}}