{"action":"created","comment":{"url":"https://api.github.com/repos/torchbox/wagtail-rfcs/pulls/comments/62409831","id":62409831,"diff_hunk":"@@ -0,0 +1,215 @@\n+========================\n+RFC 9: Page translations\n+========================\n+\n+:RFC: 9\n+:Author: Tim Heap\n+:Status: Draft\n+:Created: 2016-05-05\n+:Last-Modified: 2016-05-05\n+\n+.. contents:: Table of Contents\n+   :depth: 3\n+   :local:\n+\n+Abstract\n+========\n+\n+Translations of pages in Wagtail currently has no officially supported method.\n+Many people have implemented translations in many different methods,\n+some more or less elegant than others.\n+All of the current methods suffer from not being integrated with Wagtail itself.\n+This proposal is for adding official, first-class support for translations in Wagtail.\n+\n+Background\n+==========\n+\n+People around the world speak an amazing array of languages.\n+Wagtail, if it wants to stay relevant in a multilingual world\n+needs to support multilingual sites.\n+\n+There are a number of approaches to translations for Wagtail pages\n+out in the wild currently.\n+None of these approaches are optimal,\n+and none are officially supported or recommended by Wagtail.\n+An official, supported method that has support in Wagtail core is required.\n+\n+Requirements\n+============\n+\n+* Backwards compatible with current Wagtail sites,\n+  although these sites and pages will not translatable.\n+\n+* It must be possible to translate content in to multiple languages.\n+\n+* Nicely behaved in the database\n+\n+  * No extraneous model instances\n+\n+  * Translations have explicit relations to each other at the database level\n+\n+  * Able to use all features of the database (validation, data types, etc) and migrations - i.e. no JSON blobs for translated content\n+\n+* Nice to use as a developer / template author\n+\n+  * Fairly seamless, in terms of defining models and edit handlers\n+\n+  * A clear distinction between translated and non-translated fields\n+\n+  * Easy to find all translations of a page\n+\n+* Nice to use as an editor\n+\n+  * The behaviour of editing pages not significantly changed\n+\n+  * Adding a translation is easy\n+\n+  * Different translations can be reviewed, moderated, approved, and published separately\n+\n+  * Adding a language can be done through the admin\n+\n+What is not required\n+--------------------\n+\n+* Translating non-page models\n+\n+Current approaches\n+==================\n+\n+One method involves manually duplicating the Page tree, having one tree per language.\n+Keeping the trees in sync is difficult,\n+and means that all pages need to be translated to properly keep the trees in sync.\n+\n+One method involves adding fields for each supported language to the model,\n+such as ``body_en`` and ``body_fr``.\n+This makes for a very verbose model declaration,\n+a very large edit page, and requires each page to be completely translated\n+to pass validation.\n+\n+Praekelt have developed a method involving one page per translation,\n+but in a way that does not require duplicate trees.\n+Instead, the translated page is inserted as a sibling of the base page,\n+and then hidden from view.\n+This has the downside of needing to translate every field in a model,\n+even for fields such as background images that should be consistent across translations.\n+\n+Proposal\n+========\n+\n+Models\n+------\n+\n+A few model classes will be provided.\n+\n+A ``Language`` model will be created as part of the translations app.","path":"draft/0009-translations.rst","position":104,"original_position":104,"commit_id":"98cd1b351880c0af649ac2da22078a742297a5f2","original_commit_id":"98cd1b351880c0af649ac2da22078a742297a5f2","user":{"login":"timheap","id":132147,"avatar_url":"https://avatars.githubusercontent.com/u/132147?v=3","gravatar_id":"","url":"https://api.github.com/users/timheap","html_url":"https://github.com/timheap","followers_url":"https://api.github.com/users/timheap/followers","following_url":"https://api.github.com/users/timheap/following{/other_user}","gists_url":"https://api.github.com/users/timheap/gists{/gist_id}","starred_url":"https://api.github.com/users/timheap/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/timheap/subscriptions","organizations_url":"https://api.github.com/users/timheap/orgs","repos_url":"https://api.github.com/users/timheap/repos","events_url":"https://api.github.com/users/timheap/events{/privacy}","received_events_url":"https://api.github.com/users/timheap/received_events","type":"User","site_admin":false},"body":"I had considered Django's `LANGUAGES`, and I do actually use it in a few locations as well. It has limitations though, as you can not set a language to be in draft mode, or enable/disable them easily as needed. When setting up a `Language` instance, you have to select one of the languages from `LANGUAGES` for that language instance to represent. You can also give it a display name (potentially different from the name from `LANGUAGES`), and enable/disable it for choice from the front end. I'll update the RFC to reflect this information.","created_at":"2016-05-07T02:19:39Z","updated_at":"2016-05-07T02:19:39Z","html_url":"https://github.com/torchbox/wagtail-rfcs/pull/9#discussion_r62409831","pull_request_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/pulls/9","_links":{"self":{"href":"https://api.github.com/repos/torchbox/wagtail-rfcs/pulls/comments/62409831"},"html":{"href":"https://github.com/torchbox/wagtail-rfcs/pull/9#discussion_r62409831"},"pull_request":{"href":"https://api.github.com/repos/torchbox/wagtail-rfcs/pulls/9"}}},"pull_request":{"url":"https://api.github.com/repos/torchbox/wagtail-rfcs/pulls/9","id":68996129,"html_url":"https://github.com/torchbox/wagtail-rfcs/pull/9","diff_url":"https://github.com/torchbox/wagtail-rfcs/pull/9.diff","patch_url":"https://github.com/torchbox/wagtail-rfcs/pull/9.patch","issue_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/issues/9","number":9,"state":"open","locked":false,"title":"0009 Translatable pages","user":{"login":"timheap","id":132147,"avatar_url":"https://avatars.githubusercontent.com/u/132147?v=3","gravatar_id":"","url":"https://api.github.com/users/timheap","html_url":"https://github.com/timheap","followers_url":"https://api.github.com/users/timheap/followers","following_url":"https://api.github.com/users/timheap/following{/other_user}","gists_url":"https://api.github.com/users/timheap/gists{/gist_id}","starred_url":"https://api.github.com/users/timheap/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/timheap/subscriptions","organizations_url":"https://api.github.com/users/timheap/orgs","repos_url":"https://api.github.com/users/timheap/repos","events_url":"https://api.github.com/users/timheap/events{/privacy}","received_events_url":"https://api.github.com/users/timheap/received_events","type":"User","site_admin":false},"body":"[Rendered view](https://github.com/takeflight/wagtail-rfcs/blob/0008-translations/draft/0009-translations.rst)","created_at":"2016-05-05T11:40:13Z","updated_at":"2016-05-07T02:19:39Z","closed_at":null,"merged_at":null,"merge_commit_sha":"911763d753f340ed609f038bd8c1789e551b18e0","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/pulls/9/commits","review_comments_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/pulls/9/comments","review_comment_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/pulls/comments{/number}","comments_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/issues/9/comments","statuses_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/statuses/98cd1b351880c0af649ac2da22078a742297a5f2","head":{"label":"takeflight:0008-translations","ref":"0008-translations","sha":"98cd1b351880c0af649ac2da22078a742297a5f2","user":{"login":"takeflight","id":7663899,"avatar_url":"https://avatars.githubusercontent.com/u/7663899?v=3","gravatar_id":"","url":"https://api.github.com/users/takeflight","html_url":"https://github.com/takeflight","followers_url":"https://api.github.com/users/takeflight/followers","following_url":"https://api.github.com/users/takeflight/following{/other_user}","gists_url":"https://api.github.com/users/takeflight/gists{/gist_id}","starred_url":"https://api.github.com/users/takeflight/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/takeflight/subscriptions","organizations_url":"https://api.github.com/users/takeflight/orgs","repos_url":"https://api.github.com/users/takeflight/repos","events_url":"https://api.github.com/users/takeflight/events{/privacy}","received_events_url":"https://api.github.com/users/takeflight/received_events","type":"Organization","site_admin":false},"repo":{"id":57251593,"name":"wagtail-rfcs","full_name":"takeflight/wagtail-rfcs","owner":{"login":"takeflight","id":7663899,"avatar_url":"https://avatars.githubusercontent.com/u/7663899?v=3","gravatar_id":"","url":"https://api.github.com/users/takeflight","html_url":"https://github.com/takeflight","followers_url":"https://api.github.com/users/takeflight/followers","following_url":"https://api.github.com/users/takeflight/following{/other_user}","gists_url":"https://api.github.com/users/takeflight/gists{/gist_id}","starred_url":"https://api.github.com/users/takeflight/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/takeflight/subscriptions","organizations_url":"https://api.github.com/users/takeflight/orgs","repos_url":"https://api.github.com/users/takeflight/repos","events_url":"https://api.github.com/users/takeflight/events{/privacy}","received_events_url":"https://api.github.com/users/takeflight/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/takeflight/wagtail-rfcs","description":"Wagtail RFCs","fork":true,"url":"https://api.github.com/repos/takeflight/wagtail-rfcs","forks_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/forks","keys_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/teams","hooks_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/hooks","issue_events_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/issues/events{/number}","events_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/events","assignees_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/assignees{/user}","branches_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/branches{/branch}","tags_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/tags","blobs_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/git/refs{/sha}","trees_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/statuses/{sha}","languages_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/languages","stargazers_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/stargazers","contributors_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/contributors","subscribers_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/subscribers","subscription_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/subscription","commits_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/commits{/sha}","git_commits_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/git/commits{/sha}","comments_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/comments{/number}","issue_comment_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/issues/comments{/number}","contents_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/contents/{+path}","compare_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/merges","archive_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/downloads","issues_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/issues{/number}","pulls_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/pulls{/number}","milestones_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/milestones{/number}","notifications_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/labels{/name}","releases_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/releases{/id}","deployments_url":"https://api.github.com/repos/takeflight/wagtail-rfcs/deployments","created_at":"2016-04-27T21:56:48Z","updated_at":"2016-03-27T10:01:39Z","pushed_at":"2016-05-05T11:42:07Z","git_url":"git://github.com/takeflight/wagtail-rfcs.git","ssh_url":"git@github.com:takeflight/wagtail-rfcs.git","clone_url":"https://github.com/takeflight/wagtail-rfcs.git","svn_url":"https://github.com/takeflight/wagtail-rfcs","homepage":"","size":19,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"torchbox:master","ref":"master","sha":"3a9c599b381216cf90e739893e46b49737ba197b","user":{"login":"torchbox","id":84015,"avatar_url":"https://avatars.githubusercontent.com/u/84015?v=3","gravatar_id":"","url":"https://api.github.com/users/torchbox","html_url":"https://github.com/torchbox","followers_url":"https://api.github.com/users/torchbox/followers","following_url":"https://api.github.com/users/torchbox/following{/other_user}","gists_url":"https://api.github.com/users/torchbox/gists{/gist_id}","starred_url":"https://api.github.com/users/torchbox/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/torchbox/subscriptions","organizations_url":"https://api.github.com/users/torchbox/orgs","repos_url":"https://api.github.com/users/torchbox/repos","events_url":"https://api.github.com/users/torchbox/events{/privacy}","received_events_url":"https://api.github.com/users/torchbox/received_events","type":"Organization","site_admin":false},"repo":{"id":40007822,"name":"wagtail-rfcs","full_name":"torchbox/wagtail-rfcs","owner":{"login":"torchbox","id":84015,"avatar_url":"https://avatars.githubusercontent.com/u/84015?v=3","gravatar_id":"","url":"https://api.github.com/users/torchbox","html_url":"https://github.com/torchbox","followers_url":"https://api.github.com/users/torchbox/followers","following_url":"https://api.github.com/users/torchbox/following{/other_user}","gists_url":"https://api.github.com/users/torchbox/gists{/gist_id}","starred_url":"https://api.github.com/users/torchbox/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/torchbox/subscriptions","organizations_url":"https://api.github.com/users/torchbox/orgs","repos_url":"https://api.github.com/users/torchbox/repos","events_url":"https://api.github.com/users/torchbox/events{/privacy}","received_events_url":"https://api.github.com/users/torchbox/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/torchbox/wagtail-rfcs","description":"Wagtail RFCs","fork":false,"url":"https://api.github.com/repos/torchbox/wagtail-rfcs","forks_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/forks","keys_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/teams","hooks_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/hooks","issue_events_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/issues/events{/number}","events_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/events","assignees_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/assignees{/user}","branches_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/branches{/branch}","tags_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/tags","blobs_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/git/refs{/sha}","trees_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/statuses/{sha}","languages_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/languages","stargazers_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/stargazers","contributors_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/contributors","subscribers_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/subscribers","subscription_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/subscription","commits_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/commits{/sha}","git_commits_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/git/commits{/sha}","comments_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/comments{/number}","issue_comment_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/issues/comments{/number}","contents_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/contents/{+path}","compare_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/merges","archive_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/downloads","issues_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/issues{/number}","pulls_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/pulls{/number}","milestones_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/milestones{/number}","notifications_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/labels{/name}","releases_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/releases{/id}","deployments_url":"https://api.github.com/repos/torchbox/wagtail-rfcs/deployments","created_at":"2015-07-31T13:58:46Z","updated_at":"2016-04-30T05:24:41Z","pushed_at":"2016-05-05T11:42:07Z","git_url":"git://github.com/torchbox/wagtail-rfcs.git","ssh_url":"git@github.com:torchbox/wagtail-rfcs.git","clone_url":"https://github.com/torchbox/wagtail-rfcs.git","svn_url":"https://github.com/torchbox/wagtail-rfcs","homepage":"","size":20,"stargazers_count":3,"watchers_count":3,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":5,"forks":5,"open_issues":5,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/torchbox/wagtail-rfcs/pulls/9"},"html":{"href":"https://github.com/torchbox/wagtail-rfcs/pull/9"},"issue":{"href":"https://api.github.com/repos/torchbox/wagtail-rfcs/issues/9"},"comments":{"href":"https://api.github.com/repos/torchbox/wagtail-rfcs/issues/9/comments"},"review_comments":{"href":"https://api.github.com/repos/torchbox/wagtail-rfcs/pulls/9/comments"},"review_comment":{"href":"https://api.github.com/repos/torchbox/wagtail-rfcs/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/torchbox/wagtail-rfcs/pulls/9/commits"},"statuses":{"href":"https://api.github.com/repos/torchbox/wagtail-rfcs/statuses/98cd1b351880c0af649ac2da22078a742297a5f2"}}}}