{"action":"created","issue":{"url":"https://api.github.com/repos/phetsims/phetcommon/issues/36","repository_url":"https://api.github.com/repos/phetsims/phetcommon","labels_url":"https://api.github.com/repos/phetsims/phetcommon/issues/36/labels{/name}","comments_url":"https://api.github.com/repos/phetsims/phetcommon/issues/36/comments","events_url":"https://api.github.com/repos/phetsims/phetcommon/issues/36/events","html_url":"https://github.com/phetsims/phetcommon/issues/36","id":209544244,"number":36,"title":"new approach to placeholders in translated strings","user":{"login":"pixelzoom","id":3046552,"avatar_url":"https://avatars2.githubusercontent.com/u/3046552?v=3","gravatar_id":"","url":"https://api.github.com/users/pixelzoom","html_url":"https://github.com/pixelzoom","followers_url":"https://api.github.com/users/pixelzoom/followers","following_url":"https://api.github.com/users/pixelzoom/following{/other_user}","gists_url":"https://api.github.com/users/pixelzoom/gists{/gist_id}","starred_url":"https://api.github.com/users/pixelzoom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pixelzoom/subscriptions","organizations_url":"https://api.github.com/users/pixelzoom/orgs","repos_url":"https://api.github.com/users/pixelzoom/repos","events_url":"https://api.github.com/users/pixelzoom/events{/privacy}","received_events_url":"https://api.github.com/users/pixelzoom/received_events","type":"User","site_admin":false},"labels":[{"id":322178369,"url":"https://api.github.com/repos/phetsims/phetcommon/labels/priority:4-low","name":"priority:4-low","color":"c7def8","default":false}],"state":"open","locked":false,"assignee":{"login":"pixelzoom","id":3046552,"avatar_url":"https://avatars2.githubusercontent.com/u/3046552?v=3","gravatar_id":"","url":"https://api.github.com/users/pixelzoom","html_url":"https://github.com/pixelzoom","followers_url":"https://api.github.com/users/pixelzoom/followers","following_url":"https://api.github.com/users/pixelzoom/following{/other_user}","gists_url":"https://api.github.com/users/pixelzoom/gists{/gist_id}","starred_url":"https://api.github.com/users/pixelzoom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pixelzoom/subscriptions","organizations_url":"https://api.github.com/users/pixelzoom/orgs","repos_url":"https://api.github.com/users/pixelzoom/repos","events_url":"https://api.github.com/users/pixelzoom/events{/privacy}","received_events_url":"https://api.github.com/users/pixelzoom/received_events","type":"User","site_admin":false},"assignees":[{"login":"pixelzoom","id":3046552,"avatar_url":"https://avatars2.githubusercontent.com/u/3046552?v=3","gravatar_id":"","url":"https://api.github.com/users/pixelzoom","html_url":"https://github.com/pixelzoom","followers_url":"https://api.github.com/users/pixelzoom/followers","following_url":"https://api.github.com/users/pixelzoom/following{/other_user}","gists_url":"https://api.github.com/users/pixelzoom/gists{/gist_id}","starred_url":"https://api.github.com/users/pixelzoom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pixelzoom/subscriptions","organizations_url":"https://api.github.com/users/pixelzoom/orgs","repos_url":"https://api.github.com/users/pixelzoom/repos","events_url":"https://api.github.com/users/pixelzoom/events{/privacy}","received_events_url":"https://api.github.com/users/pixelzoom/received_events","type":"User","site_admin":false}],"milestone":null,"comments":4,"created_at":"2017-02-22T18:44:52Z","updated_at":"2017-04-12T19:23:52Z","closed_at":null,"body":"We're currently using Java's MessageFormat syntax for filling in placeholders in translated strings (e.g. `\"{0} for {1}?\"`). We did this because it was easy to implement (see `StringUtils.format`) and made it easy to port Java code and strings.\r\n\r\nIn a Skype discussion about such strings, we identified a few issues with this approach:\r\n\r\n1. It's difficult for translators to determine the semantics of the placeholders. We've attempted (inconsistently) to do that via string key names (e.g. \"pattern.0items.for.1cost\").  Longterm would be to add metadata that describes the placeholders, and present that metadata to translators in Rosetta. But that's unlikely to happen in the near future.\r\n\r\n2. The semantics of placeholders may similarly be unclear to developers. And when calling StringUtils.format, you'd better have the placeholders in the correct order.\r\n\r\n@jonathanolson suggested a new approach, @samreid and I gave additional input. The new approach:\r\n\r\nInstead of numeric placeholders, use named placeholders, with names chosen to describe the placeholder's semantics. Placeholders would be delimited with double curly braces (`\"{{units}}\"`), as is done in simula-rasa and other templates. E.g. in a string file:\r\n\r\n```js\r\n  \"itemsForCost\": {\r\n    \"{{numberOfItems}} for ${{cost}}?\"\r\n  },\r\n```\r\n\r\nAt call sites, the placeholder values would be specified via a hash whose keys match the placeholder names. E.g.:\r\n\r\n```js\r\nvar itemsForCostString = require( 'string!../itemsForCost' );\r\n...\r\n// 'Apples of $10?'\r\nvar myString = StringUtils.fillIn( itemsForCostString, {\r\n  numberOfItems: 'Apples',\r\n  cost: 10\r\n} );\r\n```\r\n\r\nQuestions:\r\n• There's no info about the type of the placeholders in this approach, is that OK?\r\n• Rosetta will need to validate the translated string, to make sure that it contains all of the same placeholders as the English string. What would it take to do that?\r\n• Would we deprecate `StringUtils.format`?\r\n• Would we replace strings that use the old format? That would involve changing English and translated strings, and probably string keys.\r\n• What to name the new formatting function? (I called it `StringUtils.fillIn` in the example above.)"},"comment":{"url":"https://api.github.com/repos/phetsims/phetcommon/issues/comments/293681421","html_url":"https://github.com/phetsims/phetcommon/issues/36#issuecomment-293681421","issue_url":"https://api.github.com/repos/phetsims/phetcommon/issues/36","id":293681421,"user":{"login":"pixelzoom","id":3046552,"avatar_url":"https://avatars2.githubusercontent.com/u/3046552?v=3","gravatar_id":"","url":"https://api.github.com/users/pixelzoom","html_url":"https://github.com/pixelzoom","followers_url":"https://api.github.com/users/pixelzoom/followers","following_url":"https://api.github.com/users/pixelzoom/following{/other_user}","gists_url":"https://api.github.com/users/pixelzoom/gists{/gist_id}","starred_url":"https://api.github.com/users/pixelzoom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pixelzoom/subscriptions","organizations_url":"https://api.github.com/users/pixelzoom/orgs","repos_url":"https://api.github.com/users/pixelzoom/repos","events_url":"https://api.github.com/users/pixelzoom/events{/privacy}","received_events_url":"https://api.github.com/users/pixelzoom/received_events","type":"User","site_admin":false},"created_at":"2017-04-12T19:23:52Z","updated_at":"2017-04-12T19:23:52Z","body":"This feature is now implemented, along with unit tests in phetcommon/tests/qunit/.\r\n\r\n@jonathanolson would you mind reviewing, since you've done quite a bit of work in StringUtils in the past?"}}
{"actor":{"display_login":"pixelzoom"}}