{"action":"created","comment":{"url":"https://api.github.com/repos/transifex/openformats/pulls/comments/64026303","id":64026303,"diff_hunk":"@@ -0,0 +1,427 @@\n+from __future__ import absolute_import\n+\n+import itertools\n+from xml.sax import saxutils\n+\n+from ..handlers import Handler\n+from ..strings import OpenString\n+from ..utils.xml import NewDumbXml\n+from ..exceptions import RuleError\n+from ..transcribers import Transcriber\n+from ..utils.xmlutils import XMLUtils, reraise_syntax_as_parse_errors\n+\n+\n+class StringsDictHandler(Handler):\n+    \"\"\"A handler class that parses and compiles `.stringsdict` files for iOS\n+    applications. The `.stringsdict` file is in XML format.\n+\n+    `.stringsdict` file documentation can be found here:\n+    https://developer.apple.com/library/ios/documentation/MacOSX/Conceptual/BPInternational/StringsdictFileFormat/StringsdictFileFormat.html\n+    \"\"\"\n+\n+    name = \"STRINGSDICT\"\n+    extension = \"stringsdict\"\n+\n+    EXTRACTS_RAW = False\n+\n+    # Where to start parsing the file\n+    PARSE_START = \"<dict\"\n+\n+    # Ignored keys\n+    KEY_FORMAT = 'NSStringLocalizedFormatKey'\n+    KEY_SPEC = 'NSStringFormatSpecTypeKey'\n+    KEY_VALUE = 'NSStringFormatValueTypeKey'\n+\n+    # Placeholders\n+    PLACEHOLDER_KEY_TAG = 'tx_awesome_key_tag'\n+    PLACEHOLDER_KEY = '<tx_awesome_key_tag></tx_awesome_key_tag>'\n+    PLACEHOLDER_STRING_TAG = 'tx_awesome_string_tag'\n+    PLACEHOLDER_STRING = '<tx_awesome_string_tag>{}</tx_awesome_string_tag>'\n+\n+    # Complile templates\n+    KEY_TEMPLATE = u'<key>{rule_string}</key>'\n+    STRING_TEMPLATE = u'<string>{plural_string}</string>'\n+\n+    # Relevant tags\n+    STRING = \"string\"\n+    DICT = \"dict\"\n+    KEY = \"key\"\n+\n+    \"\"\" Parse Methods \"\"\"\n+\n+    @reraise_syntax_as_parse_errors\n+    def parse(self, content):\n+        self.transcriber = Transcriber(content)\n+        self.order_counter = itertools.count()\n+        source = self.transcriber.source\n+        # Skip xml info declaration\n+        dict_tag_position = source.index(self.PARSE_START)\n+\n+        parsed = NewDumbXml(source, dict_tag_position)\n+        XMLUtils.validate_no_text_characters(self.transcriber, parsed)\n+        XMLUtils.validate_no_tail_characters(self.transcriber, parsed)\n+        dict_iterator = parsed.find_children()\n+        stringset = []\n+        self.main_keys = set()\n+        self.existing_hashes = set()\n+\n+        for key_tag in dict_iterator:\n+            dict_child = self._get_key_value(dict_iterator, key_tag)\n+            stringset.extend(\n+                self._handle_child_pairs(key_tag, dict_child)\n+            )\n+\n+        # Finish copying and create template\n+        self.transcriber.copy_until(len(source))\n+        template = self.transcriber.get_destination()\n+\n+        return template, stringset\n+\n+    def _handle_child_pairs(self, key_tag, dict_tag):\n+        \"\"\"Handles the <key> tag and its <dict> value tag.\n+\n+        :param key_tag: The <key> tag to be handled.\n+        :param dict_tag: The <dict> tag to be handled.\n+        :returns: A list containing the openstrings created. If no strings were\n+                    created the list is empty.\n+        \"\"\"\n+        # The first key tag contains the main key\n+        main_key = self._handle_key(key_tag, main_key=True)\n+        dict_iterator = self._handle_dict(dict_tag)\n+\n+        string_list = []\n+        for key_child in dict_iterator:\n+            # The second key contains the secondary key.\n+            secondary_key = self._handle_key(key_child)\n+            value_tag = self._get_key_value(dict_iterator, key_child)\n+            if secondary_key == self.KEY_FORMAT:\n+                # If the key is the one of the stringsdict defaults skip it\n+                continue\n+\n+            openstring = self._handle_strings(\n+                value_tag,\n+                main_key,\n+                secondary_key\n+            )\n+            if openstring is not None:\n+                # If an openstring was created append it to the list\n+                string_list.append(openstring)\n+        return string_list\n+\n+    def _handle_strings(self, dict_tag, main_key, secondary_key):\n+        \"\"\"Handles the <dict> tag that contains the strings.\n+\n+        :param dict_tag: The <dict> tag containing the strings.\n+        :param main_key: The main key. Used as the openstring's name.\n+        :param secondary_key: The secondary key. Used as the openstring's\n+                                context.\n+        \"\"\"\n+        dict_iterator = self._handle_dict(dict_tag)\n+        self.transcriber.copy_until(dict_tag.position)\n+        line_number = self.transcriber.line_number\n+        if dict_tag.text is not None:\n+            self.transcriber.copy_until(\n+                dict_tag.text_position + len(dict_tag.text)\n+            )\n+\n+        strings_dict = {}\n+        for key_tag in dict_iterator:\n+            key_content = self._handle_key(key_tag)\n+            value_tag = self._get_key_value(dict_iterator, key_tag)\n+\n+            if key_content in [self.KEY_SPEC, self.KEY_VALUE]:\n+                self.transcriber.copy_until(value_tag.tail_position)\n+                continue\n+\n+            # Get rule number from the key content\n+            rule_number = self._validate_plural(key_content, key_tag)\n+            strings_dict[rule_number] = value_tag.content\n+\n+            # If empty <string> tag keep it. It is either a placeholder which\n+            # should be kept or it is missing plurals and we will raise a\n+            # ParseError on self._create_string.\n+            if value_tag.content and value_tag.content.strip() != \"\":\n+                self.transcriber.skip_until(value_tag.end)\n+            else:\n+                self.transcriber.copy_until(value_tag.tail_position)\n+\n+        openstring = self._create_string(\n+            main_key,\n+            strings_dict,\n+            secondary_key,\n+            dict_tag,\n+            line_number\n+        )\n+\n+        if openstring is not None:\n+            self.transcriber.add(u''.join([\n+                key_tag.tail,\n+                self.PLACEHOLDER_KEY,\n+                key_tag.tail,\n+                self.PLACEHOLDER_STRING.format(\n+                    openstring.template_replacement\n+                ),\n+                value_tag.tail\n+            ]))\n+        self.transcriber.copy_until(dict_tag.end)\n+        return openstring\n+\n+    def _create_string(self, main_key, strings_dict, secondary_key, tag,\n+                       line_number):\n+        \"\"\"Validates and creates an OpenString.\n+\n+        :main_key: The name of the OpenString.\n+        :strings_dict: A dictionary containing the plurals of the OpenString.\n+        :secondary_key: The context of the OpenString.\n+        :returns: An OpenString if one was created else None.\n+        \"\"\"\n+        string_not_empty = XMLUtils.validate_not_empty_string(\n+            self.transcriber,\n+            strings_dict,\n+            tag,\n+            error_context={\n+                'line_number': line_number,\n+                'child_tag': 'string',\n+                'main_tag': tag.tag\n+            }\n+        )\n+        if string_not_empty:\n+            openstring = OpenString(\n+                main_key,\n+                strings_dict,\n+                context=secondary_key,\n+                order=self.order_counter.next(),\n+                pluralized=True\n+            )\n+            return openstring\n+        return None\n+\n+    def _validate_plural(self, plural_rule, key_tag):\n+        \"\"\"Validates the plural rule.\n+\n+        :param plural_rule: The string containing the plural rule.\n+        :key_tag: The <key> tag that contains the plural rule.\n+                    Used when validation fails to track the line number.\n+        :raises: ParseError when the plural_rule is invalid.\n+        :returns: An integer that matches the plural rule.\n+        \"\"\"\n+        try:\n+            rule_number = self.get_rule_number(plural_rule)\n+        except RuleError:\n+            message = (\n+                u\"The plural <key> tag on line {line_number} contains \"\n+                u\"an invalid plural rule: `{rule}`\"\n+            )\n+            XMLUtils.raise_error(\n+                self.transcriber,\n+                key_tag,\n+                message,\n+                context={'rule': plural_rule}\n+            )\n+\n+        return rule_number\n+\n+    def _handle_key(self, key_tag, main_key=False):\n+        \"\"\"Validates a <key> tag and returns it's content.\n+\n+        :param key_tag: The <key> tag to validate.\n+        :param main_key: Specifies a key as main. If True it also validates\n+                            the key uniqueness.\n+        :raises: ParseError if the tag is not a <key>. Also raises a ParseError\n+                    if main_key is True and the key is not unique.\n+        :returns: A string containing the <key> tags content.\n+        \"\"\"\n+        if key_tag.tag != self.KEY:\n+            message = (\n+                u\"Was expecting <key> tag but found <{tag}> tag \"\n+                u\"on line {line_number}\"\n+            )\n+            XMLUtils.raise_error(\n+                self.transcriber,\n+                key_tag,\n+                message,\n+                context={'tag': key_tag.tag}\n+            )\n+\n+        key_content = key_tag.content\n+        if main_key:\n+            if key_content in self.main_keys:\n+                message = (\n+                    u\"Duplicate main key ({main_key}) \"\n+                    u\"found on line {line_number}\"\n+                )\n+                XMLUtils.raise_error(\n+                    self.transcriber,\n+                    key_tag,\n+                    message,\n+                    context={'main_key': key_content}\n+                )\n+            self.main_keys.add(key_content)\n+\n+        return key_content\n+\n+    def _handle_dict(self, dict_tag):\n+        \"\"\"Validates a <dict> tag and returns it's children iterator.\n+\n+        :param dict_tag: The <dict> tag to validate.\n+        :raises: ParseError if the tag is not <dict>.\n+        :returns: An iterator containing the children of the <dict> tag.\n+        \"\"\"\n+        if dict_tag.tag != self.DICT:\n+            message = (\n+                u\"Was expecting <dict> tag but found <{tag}> tag on line \"\n+                u\"{line_number}\"\n+            )\n+            XMLUtils.raise_error(\n+                self.transcriber,\n+                dict_tag,\n+                message,\n+                context={'tag': dict_tag.tag}\n+            )\n+        return dict_tag.find_children()\n+\n+    \"\"\" Compile Methods \"\"\"\n+\n+    def compile(self, template, stringset):\n+        dict_tag_position = template.index(self.PARSE_START)\n+\n+        self.transcriber = Transcriber(template[dict_tag_position:])\n+        source = self.transcriber.source\n+\n+        parsed = NewDumbXml(source)\n+        dict_iterator = parsed.find_children(self.DICT)\n+\n+        self.stringset = iter(stringset)\n+        self.next_string = self._get_next_string()\n+        for dict_tag in dict_iterator:\n+            self._compile_dict(dict_tag)\n+\n+        self.transcriber.copy_until(len(source))\n+        compiled = template[:dict_tag_position] +\\\n+            self.transcriber.get_destination()\n+\n+        return compiled\n+\n+    def _compile_dict(self, dict_tag):\n+        \"\"\"Compiles the `dict` tags that contains the translations.\n+\n+        :param dict_tag: The main dict tag that contains the child dict tags\n+        which in turn contain the placeholders for the translations.\n+        If no translations are found for a child's placeholders\n+        the whole child dict is ommited.\n+        \"\"\"\n+\n+        if dict_tag.text is not None:\n+            self.transcriber.copy_until(\n+                dict_tag.text_position + len(dict_tag.text)\n+            )\n+\n+        key_value_iterator = dict_tag.find_children()\n+        for key_tag in key_value_iterator:\n+            value_tag = self._get_key_value(key_value_iterator, key_tag)\n+            if value_tag.tag == self.DICT:\n+                placeholder_list = list(value_tag.find_children(\n+                    self.PLACEHOLDER_KEY_TAG, self.PLACEHOLDER_STRING_TAG\n+                ))\n+                should_compile = (\n+                    len(placeholder_list) and\n+                    XMLUtils.should_compile(\n+                        placeholder_list[-1],\n+                        self.next_string)\n+                )\n+                if should_compile:\n+                    placeholder_iterator = value_tag.find_children(\n+                        self.PLACEHOLDER_KEY_TAG, self.PLACEHOLDER_STRING_TAG\n+                    )\n+                    for placeholder_key in placeholder_iterator:\n+                        placeholder_value = self._get_key_value(\n+                            placeholder_iterator,\n+                            placeholder_key\n+                        )\n+                        # Copy until the placeholder_key\n+                        self.transcriber.copy_until(placeholder_key.position)\n+                        # Compile translation\n+                        self._compile_plural_string(\n+                            placeholder_key, placeholder_value\n+                        )\n+                        # Skip placeholder_key and placeholder_value\n+                        self.transcriber.skip_until(\n+                            placeholder_value.tail_position\n+                        )\n+                        # Copy the end to keep formating\n+                        self.transcriber.copy_until(\n+                            value_tag.tail_position\n+                        )\n+\n+                else:\n+                    if len(placeholder_list):\n+                        self.transcriber.skip_until(value_tag.tail_position)\n+                    else:\n+                        self.transcriber.copy_until(value_tag.end)\n+            else:\n+                # Else copy until the end of the value tag\n+                self.transcriber.copy_until(value_tag.tail_position)\n+        self.transcriber.copy_until(dict_tag.end)\n+\n+    def _compile_plural_string(self, placeholder_key, placeholder_value):\n+        \"\"\"Replaces the placeholder tags with the translation strings.\n+\n+        :param placeholder_key: The placeholder key tag.\n+        :param placeholder_value: The placeholder value tag.\n+        :NOTE: Assigns the self property `next_string` to the next OpenString.\n+        \"\"\"\n+        string_list = self.next_string.string.items()","path":"openformats/formats/stringsdict.py","position":373,"original_position":373,"commit_id":"c6e08f8508c9a976504314d3a8aa6138f0a990ba","original_commit_id":"c6e08f8508c9a976504314d3a8aa6138f0a990ba","user":{"login":"kbairak","id":1336464,"avatar_url":"https://avatars.githubusercontent.com/u/1336464?v=3","gravatar_id":"","url":"https://api.github.com/users/kbairak","html_url":"https://github.com/kbairak","followers_url":"https://api.github.com/users/kbairak/followers","following_url":"https://api.github.com/users/kbairak/following{/other_user}","gists_url":"https://api.github.com/users/kbairak/gists{/gist_id}","starred_url":"https://api.github.com/users/kbairak/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kbairak/subscriptions","organizations_url":"https://api.github.com/users/kbairak/orgs","repos_url":"https://api.github.com/users/kbairak/repos","events_url":"https://api.github.com/users/kbairak/events{/privacy}","received_events_url":"https://api.github.com/users/kbairak/received_events","type":"User","site_admin":false},"body":"Shouldn't these be sorted? It seems that for dicts with integer keys, `items()` returns them sorted, at least for values 0 to 5 with which we're dealing with here, but if feels more right to sort them anyway.","created_at":"2016-05-20T11:21:55Z","updated_at":"2016-05-20T11:21:55Z","html_url":"https://github.com/transifex/openformats/pull/49#discussion_r64026303","pull_request_url":"https://api.github.com/repos/transifex/openformats/pulls/49","_links":{"self":{"href":"https://api.github.com/repos/transifex/openformats/pulls/comments/64026303"},"html":{"href":"https://github.com/transifex/openformats/pull/49#discussion_r64026303"},"pull_request":{"href":"https://api.github.com/repos/transifex/openformats/pulls/49"}}},"pull_request":{"url":"https://api.github.com/repos/transifex/openformats/pulls/49","id":70327992,"html_url":"https://github.com/transifex/openformats/pull/49","diff_url":"https://github.com/transifex/openformats/pull/49.diff","patch_url":"https://github.com/transifex/openformats/pull/49.patch","issue_url":"https://api.github.com/repos/transifex/openformats/issues/49","number":49,"state":"open","locked":false,"title":"DNM: Parsing/Compiling/Some testing for the stringsdict format.","user":{"login":"kbairak","id":1336464,"avatar_url":"https://avatars.githubusercontent.com/u/1336464?v=3","gravatar_id":"","url":"https://api.github.com/users/kbairak","html_url":"https://github.com/kbairak","followers_url":"https://api.github.com/users/kbairak/followers","following_url":"https://api.github.com/users/kbairak/following{/other_user}","gists_url":"https://api.github.com/users/kbairak/gists{/gist_id}","starred_url":"https://api.github.com/users/kbairak/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kbairak/subscriptions","organizations_url":"https://api.github.com/users/kbairak/orgs","repos_url":"https://api.github.com/users/kbairak/repos","events_url":"https://api.github.com/users/kbairak/events{/privacy}","received_events_url":"https://api.github.com/users/kbairak/received_events","type":"User","site_admin":false},"body":"Opening for comments","created_at":"2016-05-17T09:48:39Z","updated_at":"2016-05-20T11:21:55Z","closed_at":null,"merged_at":null,"merge_commit_sha":"46409d55d69b0f0e728fc09347010b13bb1cea9c","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/transifex/openformats/pulls/49/commits","review_comments_url":"https://api.github.com/repos/transifex/openformats/pulls/49/comments","review_comment_url":"https://api.github.com/repos/transifex/openformats/pulls/comments{/number}","comments_url":"https://api.github.com/repos/transifex/openformats/issues/49/comments","statuses_url":"https://api.github.com/repos/transifex/openformats/statuses/c6e08f8508c9a976504314d3a8aa6138f0a990ba","head":{"label":"transifex:stringsdict-devel","ref":"stringsdict-devel","sha":"c6e08f8508c9a976504314d3a8aa6138f0a990ba","user":{"login":"transifex","id":22542,"avatar_url":"https://avatars.githubusercontent.com/u/22542?v=3","gravatar_id":"","url":"https://api.github.com/users/transifex","html_url":"https://github.com/transifex","followers_url":"https://api.github.com/users/transifex/followers","following_url":"https://api.github.com/users/transifex/following{/other_user}","gists_url":"https://api.github.com/users/transifex/gists{/gist_id}","starred_url":"https://api.github.com/users/transifex/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/transifex/subscriptions","organizations_url":"https://api.github.com/users/transifex/orgs","repos_url":"https://api.github.com/users/transifex/repos","events_url":"https://api.github.com/users/transifex/events{/privacy}","received_events_url":"https://api.github.com/users/transifex/received_events","type":"Organization","site_admin":false},"repo":{"id":34167510,"name":"openformats","full_name":"transifex/openformats","owner":{"login":"transifex","id":22542,"avatar_url":"https://avatars.githubusercontent.com/u/22542?v=3","gravatar_id":"","url":"https://api.github.com/users/transifex","html_url":"https://github.com/transifex","followers_url":"https://api.github.com/users/transifex/followers","following_url":"https://api.github.com/users/transifex/following{/other_user}","gists_url":"https://api.github.com/users/transifex/gists{/gist_id}","starred_url":"https://api.github.com/users/transifex/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/transifex/subscriptions","organizations_url":"https://api.github.com/users/transifex/orgs","repos_url":"https://api.github.com/users/transifex/repos","events_url":"https://api.github.com/users/transifex/events{/privacy}","received_events_url":"https://api.github.com/users/transifex/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/transifex/openformats","description":"Opensource transifex-independent library for creating/editing/testing file formats for transifex.com","fork":false,"url":"https://api.github.com/repos/transifex/openformats","forks_url":"https://api.github.com/repos/transifex/openformats/forks","keys_url":"https://api.github.com/repos/transifex/openformats/keys{/key_id}","collaborators_url":"https://api.github.com/repos/transifex/openformats/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/transifex/openformats/teams","hooks_url":"https://api.github.com/repos/transifex/openformats/hooks","issue_events_url":"https://api.github.com/repos/transifex/openformats/issues/events{/number}","events_url":"https://api.github.com/repos/transifex/openformats/events","assignees_url":"https://api.github.com/repos/transifex/openformats/assignees{/user}","branches_url":"https://api.github.com/repos/transifex/openformats/branches{/branch}","tags_url":"https://api.github.com/repos/transifex/openformats/tags","blobs_url":"https://api.github.com/repos/transifex/openformats/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/transifex/openformats/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/transifex/openformats/git/refs{/sha}","trees_url":"https://api.github.com/repos/transifex/openformats/git/trees{/sha}","statuses_url":"https://api.github.com/repos/transifex/openformats/statuses/{sha}","languages_url":"https://api.github.com/repos/transifex/openformats/languages","stargazers_url":"https://api.github.com/repos/transifex/openformats/stargazers","contributors_url":"https://api.github.com/repos/transifex/openformats/contributors","subscribers_url":"https://api.github.com/repos/transifex/openformats/subscribers","subscription_url":"https://api.github.com/repos/transifex/openformats/subscription","commits_url":"https://api.github.com/repos/transifex/openformats/commits{/sha}","git_commits_url":"https://api.github.com/repos/transifex/openformats/git/commits{/sha}","comments_url":"https://api.github.com/repos/transifex/openformats/comments{/number}","issue_comment_url":"https://api.github.com/repos/transifex/openformats/issues/comments{/number}","contents_url":"https://api.github.com/repos/transifex/openformats/contents/{+path}","compare_url":"https://api.github.com/repos/transifex/openformats/compare/{base}...{head}","merges_url":"https://api.github.com/repos/transifex/openformats/merges","archive_url":"https://api.github.com/repos/transifex/openformats/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/transifex/openformats/downloads","issues_url":"https://api.github.com/repos/transifex/openformats/issues{/number}","pulls_url":"https://api.github.com/repos/transifex/openformats/pulls{/number}","milestones_url":"https://api.github.com/repos/transifex/openformats/milestones{/number}","notifications_url":"https://api.github.com/repos/transifex/openformats/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/transifex/openformats/labels{/name}","releases_url":"https://api.github.com/repos/transifex/openformats/releases{/id}","deployments_url":"https://api.github.com/repos/transifex/openformats/deployments","created_at":"2015-04-18T13:49:26Z","updated_at":"2016-04-29T16:39:43Z","pushed_at":"2016-05-20T08:51:52Z","git_url":"git://github.com/transifex/openformats.git","ssh_url":"git@github.com:transifex/openformats.git","clone_url":"https://github.com/transifex/openformats.git","svn_url":"https://github.com/transifex/openformats","homepage":null,"size":4163,"stargazers_count":3,"watchers_count":3,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":4,"forks":1,"open_issues":4,"watchers":3,"default_branch":"master"}},"base":{"label":"transifex:devel","ref":"devel","sha":"bfa3543a022d02ffc25fd2baafd1dc097ca72718","user":{"login":"transifex","id":22542,"avatar_url":"https://avatars.githubusercontent.com/u/22542?v=3","gravatar_id":"","url":"https://api.github.com/users/transifex","html_url":"https://github.com/transifex","followers_url":"https://api.github.com/users/transifex/followers","following_url":"https://api.github.com/users/transifex/following{/other_user}","gists_url":"https://api.github.com/users/transifex/gists{/gist_id}","starred_url":"https://api.github.com/users/transifex/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/transifex/subscriptions","organizations_url":"https://api.github.com/users/transifex/orgs","repos_url":"https://api.github.com/users/transifex/repos","events_url":"https://api.github.com/users/transifex/events{/privacy}","received_events_url":"https://api.github.com/users/transifex/received_events","type":"Organization","site_admin":false},"repo":{"id":34167510,"name":"openformats","full_name":"transifex/openformats","owner":{"login":"transifex","id":22542,"avatar_url":"https://avatars.githubusercontent.com/u/22542?v=3","gravatar_id":"","url":"https://api.github.com/users/transifex","html_url":"https://github.com/transifex","followers_url":"https://api.github.com/users/transifex/followers","following_url":"https://api.github.com/users/transifex/following{/other_user}","gists_url":"https://api.github.com/users/transifex/gists{/gist_id}","starred_url":"https://api.github.com/users/transifex/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/transifex/subscriptions","organizations_url":"https://api.github.com/users/transifex/orgs","repos_url":"https://api.github.com/users/transifex/repos","events_url":"https://api.github.com/users/transifex/events{/privacy}","received_events_url":"https://api.github.com/users/transifex/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/transifex/openformats","description":"Opensource transifex-independent library for creating/editing/testing file formats for transifex.com","fork":false,"url":"https://api.github.com/repos/transifex/openformats","forks_url":"https://api.github.com/repos/transifex/openformats/forks","keys_url":"https://api.github.com/repos/transifex/openformats/keys{/key_id}","collaborators_url":"https://api.github.com/repos/transifex/openformats/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/transifex/openformats/teams","hooks_url":"https://api.github.com/repos/transifex/openformats/hooks","issue_events_url":"https://api.github.com/repos/transifex/openformats/issues/events{/number}","events_url":"https://api.github.com/repos/transifex/openformats/events","assignees_url":"https://api.github.com/repos/transifex/openformats/assignees{/user}","branches_url":"https://api.github.com/repos/transifex/openformats/branches{/branch}","tags_url":"https://api.github.com/repos/transifex/openformats/tags","blobs_url":"https://api.github.com/repos/transifex/openformats/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/transifex/openformats/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/transifex/openformats/git/refs{/sha}","trees_url":"https://api.github.com/repos/transifex/openformats/git/trees{/sha}","statuses_url":"https://api.github.com/repos/transifex/openformats/statuses/{sha}","languages_url":"https://api.github.com/repos/transifex/openformats/languages","stargazers_url":"https://api.github.com/repos/transifex/openformats/stargazers","contributors_url":"https://api.github.com/repos/transifex/openformats/contributors","subscribers_url":"https://api.github.com/repos/transifex/openformats/subscribers","subscription_url":"https://api.github.com/repos/transifex/openformats/subscription","commits_url":"https://api.github.com/repos/transifex/openformats/commits{/sha}","git_commits_url":"https://api.github.com/repos/transifex/openformats/git/commits{/sha}","comments_url":"https://api.github.com/repos/transifex/openformats/comments{/number}","issue_comment_url":"https://api.github.com/repos/transifex/openformats/issues/comments{/number}","contents_url":"https://api.github.com/repos/transifex/openformats/contents/{+path}","compare_url":"https://api.github.com/repos/transifex/openformats/compare/{base}...{head}","merges_url":"https://api.github.com/repos/transifex/openformats/merges","archive_url":"https://api.github.com/repos/transifex/openformats/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/transifex/openformats/downloads","issues_url":"https://api.github.com/repos/transifex/openformats/issues{/number}","pulls_url":"https://api.github.com/repos/transifex/openformats/pulls{/number}","milestones_url":"https://api.github.com/repos/transifex/openformats/milestones{/number}","notifications_url":"https://api.github.com/repos/transifex/openformats/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/transifex/openformats/labels{/name}","releases_url":"https://api.github.com/repos/transifex/openformats/releases{/id}","deployments_url":"https://api.github.com/repos/transifex/openformats/deployments","created_at":"2015-04-18T13:49:26Z","updated_at":"2016-04-29T16:39:43Z","pushed_at":"2016-05-20T08:51:52Z","git_url":"git://github.com/transifex/openformats.git","ssh_url":"git@github.com:transifex/openformats.git","clone_url":"https://github.com/transifex/openformats.git","svn_url":"https://github.com/transifex/openformats","homepage":null,"size":4163,"stargazers_count":3,"watchers_count":3,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":4,"forks":1,"open_issues":4,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/transifex/openformats/pulls/49"},"html":{"href":"https://github.com/transifex/openformats/pull/49"},"issue":{"href":"https://api.github.com/repos/transifex/openformats/issues/49"},"comments":{"href":"https://api.github.com/repos/transifex/openformats/issues/49/comments"},"review_comments":{"href":"https://api.github.com/repos/transifex/openformats/pulls/49/comments"},"review_comment":{"href":"https://api.github.com/repos/transifex/openformats/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/transifex/openformats/pulls/49/commits"},"statuses":{"href":"https://api.github.com/repos/transifex/openformats/statuses/c6e08f8508c9a976504314d3a8aa6138f0a990ba"}}}}
{"actor":{"display_login":"kbairak"}}