{"action":"created","issue":{"url":"https://api.github.com/repos/SpongePowered/SpongeAPI/issues/808","labels_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/issues/808/labels{/name}","comments_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/issues/808/comments","events_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/issues/808/events","html_url":"https://github.com/SpongePowered/SpongeAPI/pull/808","id":101157141,"number":808,"title":"Improve Text API","user":{"login":"ST-DDT","id":1579362,"avatar_url":"https://avatars.githubusercontent.com/u/1579362?v=3","gravatar_id":"","url":"https://api.github.com/users/ST-DDT","html_url":"https://github.com/ST-DDT","followers_url":"https://api.github.com/users/ST-DDT/followers","following_url":"https://api.github.com/users/ST-DDT/following{/other_user}","gists_url":"https://api.github.com/users/ST-DDT/gists{/gist_id}","starred_url":"https://api.github.com/users/ST-DDT/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ST-DDT/subscriptions","organizations_url":"https://api.github.com/users/ST-DDT/orgs","repos_url":"https://api.github.com/users/ST-DDT/repos","events_url":"https://api.github.com/users/ST-DDT/events{/privacy}","received_events_url":"https://api.github.com/users/ST-DDT/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/SpongePowered/SpongeAPI/labels/needs%20review","name":"needs review","color":"eb6420"},{"url":"https://api.github.com/repos/SpongePowered/SpongeAPI/labels/text","name":"text","color":"0052cc"}],"state":"open","locked":false,"assignee":{"login":"Minecrell","id":3035868,"avatar_url":"https://avatars.githubusercontent.com/u/3035868?v=3","gravatar_id":"","url":"https://api.github.com/users/Minecrell","html_url":"https://github.com/Minecrell","followers_url":"https://api.github.com/users/Minecrell/followers","following_url":"https://api.github.com/users/Minecrell/following{/other_user}","gists_url":"https://api.github.com/users/Minecrell/gists{/gist_id}","starred_url":"https://api.github.com/users/Minecrell/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Minecrell/subscriptions","organizations_url":"https://api.github.com/users/Minecrell/orgs","repos_url":"https://api.github.com/users/Minecrell/repos","events_url":"https://api.github.com/users/Minecrell/events{/privacy}","received_events_url":"https://api.github.com/users/Minecrell/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/SpongePowered/SpongeAPI/milestones/4","html_url":"https://github.com/SpongePowered/SpongeAPI/milestones/Revision%203.0","labels_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/milestones/4/labels","id":1082492,"number":4,"title":"Revision 3.0","description":"This is the third stable revision of the Sponge API, 3.0 ","creator":{"login":"Zidane","id":744482,"avatar_url":"https://avatars.githubusercontent.com/u/744482?v=3","gravatar_id":"","url":"https://api.github.com/users/Zidane","html_url":"https://github.com/Zidane","followers_url":"https://api.github.com/users/Zidane/followers","following_url":"https://api.github.com/users/Zidane/following{/other_user}","gists_url":"https://api.github.com/users/Zidane/gists{/gist_id}","starred_url":"https://api.github.com/users/Zidane/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Zidane/subscriptions","organizations_url":"https://api.github.com/users/Zidane/orgs","repos_url":"https://api.github.com/users/Zidane/repos","events_url":"https://api.github.com/users/Zidane/events{/privacy}","received_events_url":"https://api.github.com/users/Zidane/received_events","type":"User","site_admin":false},"open_issues":19,"closed_issues":52,"state":"open","created_at":"2015-04-24T16:02:08Z","updated_at":"2015-10-07T18:34:32Z","due_on":null,"closed_at":null},"comments":24,"created_at":"2015-08-15T10:05:18Z","updated_at":"2015-10-08T17:24:11Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/SpongePowered/SpongeAPI/pulls/808","html_url":"https://github.com/SpongePowered/SpongeAPI/pull/808","diff_url":"https://github.com/SpongePowered/SpongeAPI/pull/808.diff","patch_url":"https://github.com/SpongePowered/SpongeAPI/pull/808.patch"},"body":"This PR improves the changes made in #779.\r\nImplementation: https://github.com/SpongePowered/SpongeCommon/pull/129\r\n\r\n**Additions**\r\n- `Texts.format(Text, Function<String,?>)`\r\n  - Allows lazy fetching of replacements\r\n    - Example: Directly fetch player prefixes from a permission plugin without being bound to just `prefix` or similar. `{world}{faction}{foobar}{prefix}{player}: {message}`\r\n\r\n**Changes**\r\n- `Texts.of(Object...)` now supports `Translatable` as arguments\r\n- `Text.Style#toString()` now only shows set/present values or `NONE`\r\n\r\n**Test Plugin**\r\n[TextFormat.jar](https://dl.dropboxusercontent.com/u/16999313/Sponge/TextFormat.jar) (Plugin)\r\n[TextFormat.zip](https://dl.dropboxusercontent.com/u/16999313/Sponge/TextFormat.zip) (Source)\r\n(I could not test this yet since Common is outdated)\r\nTry the following commands to see the resutls:\r\n* `/textformat`\r\n* `/textformat PvPMonster`\r\n* `/textformat PvPMonster Innocent`\r\n* `/textformat PvPMonster Innocent &6Stick of Doom`\r\n* `/textformat <YourUsername> Innocent` // Uses the item in hand\r\n* `/texttype <CatalogType/*>`\r\n* `/textjson` - Proves the json (de-)serialization works\r\n* `/textxml` - Proves the xml(de-)serialization works\r\n\r\n**Usage**\r\n\r\nConsider the following situation. You would like to show the following message to your arena participants on a regular basis.\r\n````\r\nCurrent ranking:\r\n1) MonsterKiller - 42 points (324m away)\r\n2) MunsterHunter - 21 points (123m away)\r\n3) PlayerKiller - 5 points (2m away)\r\n````\r\n\r\nWithout this PR you have basically four possibilities:\r\n**1)** Using the `TextBuilder`\r\n````java\r\nfor (Player player : participants) {\r\n    player.sendMessage(Texts.of(\"Current ranking:\"));\r\n    int index = 1;\r\n    for (Player ranked : topRanked) {\r\n        TextBuilder builder = Texts.builder();\r\n        builder.append(Texts.of(index))\r\n                .append(Texts.of(\") \"))\r\n                .append(Texts.of(ranked))\r\n                .append(Texts.of(\" - \"))\r\n                .append(Texts.of(arena.getPoints(ranked)))\r\n                .append(Texts.of(\" points (\"))\r\n                .append(Texts.of(calcDistance(player, ranked)))\r\n                .append(Texts.of(\"m away)\"));\r\n        player.sendMessage(builder.toString());\r\n    }\r\n}\r\n````\r\nThis is pretty verbose, lets reduce that a little bit.\r\n\r\n**2)** Using `Text.of(Object...)`\r\n````java\r\nfor (Player player : participants) {\r\n    player.sendMessage(Texts.of(\"Current ranking:\"));\r\n    int index = 1;\r\n    for (Player ranked : topRanked) {\r\n        Text text = Texts.of(index++, \") \", ranked, \" - \", arena.getPoints(ranked), \" points (\", calcDistance(player, ranked), \"m away)\");\r\n        player.sendMessage(text);\r\n    }\r\n}\r\n````\r\nThis is shorter and easier to read, but you have to create the same Texts over and over again. \r\nAlso you cannot use different styles for different languages.\r\n\r\n**3)** Use constants\r\n````java\r\nText MAIN_MESSAGE = Texts.of(\"Current ranking:\");\r\nText STATIC_PART1 = Texts.of(\") \");\r\nText STATIC_PART2 = Texts.of(\" - \");\r\nText STATIC_PART3 = Texts.of(\" points (\");\r\nText STATIC_PART4 = Texts.of(\"m away)\");\r\nfor (Player player : participants) {\r\n    player.sendMessage(MAIN_MESSAGE);\r\n    int index = 1;\r\n    for (Player ranked : topRanked) {\r\n        Text text = Texts.of(index++, STATIC_PART1, ranked, STATIC_PART2, arena.getPoints(ranked), STATIC_PART3, calcDistance(player, ranked), STATIC_PART4);\r\n        player.sendMessage(text);\r\n    }\r\n}\r\n````\r\nWell, we don't create the messages over and over again, but this is quite horrible to read and you still cannot use different styles for different languages.\r\nThere must be something easier.\r\n\r\n**4)** Using `Text.format(Text, X)`\r\n````java\r\nText MAIN_MESSAGE = Texts.of(\"Current ranking:\");\r\n// Text RANKING_MESSAGE = Texts.of(Texts.placeholder(\"index\"), \") \", Texts.placeholder(\"name\"), \" - \", Texts.placeholder(\"points\"), \" points (\", Texts.placeholder(\"distance\"), \"m away)\");\r\nText RANKING_MESSAGE = Texts.of(Texts.placeholder(\"0\"), \") \", Texts.placeholder(\"1\"), \" - \", Texts.placeholder(\"2\"), \" points (\", Texts.placeholder(\"3\"), \"m away)\");\r\n// Text RANKING_MESSAGE = of(placeholder(\"index\"), \") \", placeholder(\"name\"), \" - \", placeholder(\"points\"), \" points (\", placeholder(\"distance\"), \"m away)\");\r\n// Text RANKING_MESSAGE = myTextParser(\"${index}) ${name} - ${points} points (${distance}m away)\");\r\nfor (Player player : participants) {\r\n    player.sendMessage(MAIN_MESSAGE);\r\n    int index = 1;\r\n    for (Player ranked : topRanked) {\r\n        player.sendMessage(Texts.format(RANKING_MESSAGE, index++, ranked, arena.getPoints(ranked), calcDistance(player, ranked)));\r\n    }\r\n}\r\n````\r\nHell yeah, that looks pretty awesome. We also have multi language support now. We just have to exchange the `RANKING_MESSAGE`. \r\nBut wouldn't it be cool if I could just put the `points` and `distance` calculation inside the Text template?\r\n\r\n**NEW)** `Functional Placeholder`s\r\nOf course you can do that!\r\n````java\r\nText MAIN_MESSAGE = Texts.of(\"Current ranking:\");\r\n// Text RAW_RANKING_MESSAGE = Texts.of(Texts.placeholder(\"index\"), \") \", Texts.placeholder(\"name\"), \" - \", Texts.placeholder(\"points\"), \" points (\", Texts.placeholder(\"distance\"), \"m away)\");\r\nText RAW_RANKING_MESSAGE = Texts.of(Texts.placeholder(\"0\"), \") \", Texts.placeholder(\"1\"), \" - \", Texts.placeholder(\"2\"), \" points (\", Texts.placeholder(\"3\"), \"m away)\");\r\n// Text RAW_RANKING_MESSAGE = of(placeholder(\"index\"), \") \", placeholder(\"name\"), \" - \", placeholder(\"points\"), \" points (\", placeholder(\"distance\"), \"m away)\");\r\n// Text RAW_RANKING_MESSAGE = myTextParser(\"${index}) ${name} - ${points} points (${distance}m away)\");\r\nText RANKING_MESSAGE = Texts.format(RAW_RANKING_MESSAGE, null, null, \r\n        Texts.placeholder(TextArguments.<Player>transform(\"1\", (ranked) -> arena.getPoints(ranked))),\r\n        Texts.placeholder( (input) -> calcDistance(input.apply(\"3\"), input.apply(\"2\"))));\r\n// null => skip/do not replace\r\n\r\nfor (Player player : participants) {\r\n    player.sendMessage(MAIN_MESSAGE);\r\n    int index = 1;\r\n    for (Player ranked : topRanked) {\r\n        player.sendMessage(Texts.format(RANKING_MESSAGE, index++, ranked, player));\r\n    }\r\n}\r\n````\r\nYes this looks very straight forward and i probably won't ever have to change the `sendMessage` lines only the `Text` message and possibly the functions that do the customizations.\r\nBut I don't want to create a switch for multiple languages in the code, isn't it possible to push that to the `Placeholder` as well?\r\n\r\n**NEW + FANCY)** Badass `Functional Placeholder`s\r\n````java\r\nText MAIN_MESSAGE = Texts.placeholder(input -> MessageContainer.get(\"MAIN_MESSAGE\", ((Player) input.apply(\"0\")).getLocale()));\r\n// Text MAIN_MESSAGE = Texts.placeholder(new MultilanguageFunction(\"MAIN_MESSAGE\"));\r\n// Text MAIN_MESSAGE = MyMsgTools.msg(\"MAIN_MESSAGE\");\r\nText RANKING_MESSAGE = Texts.placeholder(input -> Texts.format(MessageContainer.get(\"RANKING_MESSAGE\", ((Player) input.apply(\"0\")).getLocale(), input)));\r\n// Text RANKING_MESSAGE = Texts.placeholder(new FormattedMultilanguageFunction(\"RANKING_MESSAGE\"));\r\n// Text RANKING_MESSAGE = MyMsgTools.formattedMsg(\"RANKING_MESSAGE\");\r\n\r\nfor (Player player : participants) {\r\n    player.sendMessage(Texts.format(MAIN_MESSAGE, player));\r\n    int index = 1;\r\n    for (Player ranked : topRanked) {\r\n        player.sendMessage(Texts.format(RANKING_MESSAGE, player, index++, ranked));\r\n    }\r\n}\r\n````\r\nAnd suddenly we have multi language support on the fly, we can maintain the `Text`s in their own class, and we can still read our code. With this you can almost forget that you have multi language support at all.\r\n\r\nAny questions left?\r\n\r\n**Other Suggestions**\r\n(Not part of this PR)\r\n@zml2008 \r\n[MessageEventSuggestion ](https://gist.github.com/ST-DDT/3b337cfa42b1a621e957)"},"comment":{"url":"https://api.github.com/repos/SpongePowered/SpongeAPI/issues/comments/146630158","html_url":"https://github.com/SpongePowered/SpongeAPI/pull/808#issuecomment-146630158","issue_url":"https://api.github.com/repos/SpongePowered/SpongeAPI/issues/808","id":146630158,"user":{"login":"ST-DDT","id":1579362,"avatar_url":"https://avatars.githubusercontent.com/u/1579362?v=3","gravatar_id":"","url":"https://api.github.com/users/ST-DDT","html_url":"https://github.com/ST-DDT","followers_url":"https://api.github.com/users/ST-DDT/followers","following_url":"https://api.github.com/users/ST-DDT/following{/other_user}","gists_url":"https://api.github.com/users/ST-DDT/gists{/gist_id}","starred_url":"https://api.github.com/users/ST-DDT/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ST-DDT/subscriptions","organizations_url":"https://api.github.com/users/ST-DDT/orgs","repos_url":"https://api.github.com/users/ST-DDT/repos","events_url":"https://api.github.com/users/ST-DDT/events{/privacy}","received_events_url":"https://api.github.com/users/ST-DDT/received_events","type":"User","site_admin":false},"created_at":"2015-10-08T17:24:11Z","updated_at":"2015-10-08T17:24:11Z","body":"@Phroa Thanks for checking this PR.\r\nShould be fixed now."}}