diff --git a/package.json b/package.json index 0ae6fd999b..0ee4c3e331 100644 --- a/package.json +++ b/package.json @@ -28,11 +28,13 @@ "test" ], "bin": { - "reskindex": "scripts/reskindex.js" + "reskindex": "scripts/reskindex.js", + "matrix-gen-i18n": "scripts/gen-i18n.js" }, "scripts": { "reskindex": "node scripts/reskindex.js -h header", "reskindex:watch": "node scripts/reskindex.js -h header -w", + "i18n": "matrix-gen-i18n", "build": "npm run reskindex && babel src -d lib --source-maps --copy-files", "build:watch": "babel src -w -d lib --source-maps --copy-files", "emoji-data-strip": "node scripts/emoji-data-strip.js", diff --git a/scripts/gen-i18n.js b/scripts/gen-i18n.js old mode 100644 new mode 100755 index 3ce5aeb312..609c6b00c5 --- a/scripts/gen-i18n.js +++ b/scripts/gen-i18n.js @@ -1,3 +1,5 @@ +#!/usr/bin/env node + /* Copyright 2017 New Vector Ltd @@ -34,6 +36,13 @@ const TRANSLATIONS_FUNCS = ['_t', '_td', '_tJsx']; const INPUT_TRANSLATIONS_FILE = 'src/i18n/strings/en_EN.json'; +// NB. The sync version of walk is broken for single files so we walk +// all of res rather than just res/home.html. +// https://git.daplie.com/Daplie/node-walk/merge_requests/1 fixes it, +// or if we get bored waiting for it to be merged, we could switch +// to a project that's actively maintained. +const SEARCH_PATHS = ['src', 'res']; + const FLOW_PARSER_OPTS = { esproposal_class_instance_fields: true, esproposal_class_static_fields: true, @@ -64,7 +73,7 @@ function getTKey(arg) { return null; } -function getTranslations(file) { +function getTranslationsJs(file) { const tree = flowParser.parse(fs.readFileSync(file, { encoding: 'utf8' }), FLOW_PARSER_OPTS); const trs = new Set(); @@ -106,6 +115,20 @@ function getTranslations(file) { return trs; } +function getTranslationsOther(file) { + const contents = fs.readFileSync(file, { encoding: 'utf8' }); + + const trs = new Set(); + + // Taken from riot-web src/components/structures/HomePage.js + const translationsRegex = /_t\(['"]([\s\S]*?)['"]\)/mg; + let matches; + while (matches = translationsRegex.exec(contents)) { + trs.add(matches[1]); + } + return trs; +} + // gather en_EN plural strings from the input translations file: // the en_EN strings are all in the source with the exception of // pluralised strings, which we need to pull in from elsewhere. @@ -123,20 +146,32 @@ for (const key of Object.keys(inputTranslationsRaw)) { const translatables = new Set(); -walk.walkSync("src", { +const walkOpts = { listeners: { file: function(root, fileStats, next) { - if (!fileStats.name.endsWith('.js')) return; - const fullPath = path.join(root, fileStats.name); - const trs = getTranslations(fullPath); + + let ltrs; + if (fileStats.name.endsWith('.js')) { + trs = getTranslationsJs(fullPath); + } else if (fileStats.name.endsWith('.html')) { + trs = getTranslationsOther(fullPath); + } else { + return; + } console.log(`${fullPath} (${trs.size} strings)`); for (const tr of trs.values()) { translatables.add(tr); } }, } -}); +}; + +for (const path of SEARCH_PATHS) { + if (fs.existsSync(path)) { + walk.walkSync(path, walkOpts); + } +} const trObj = {}; for (const tr of translatables) { diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index b19edc8c6e..6ce39a3716 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -490,15 +490,15 @@ export default React.createClass({ }); }, - _onNameChange: function(e) { - const newProfileForm = Object.assign(this.state.profileForm, { name: e.target.value }); + _onNameChange: function(value) { + const newProfileForm = Object.assign(this.state.profileForm, { name: value }); this.setState({ profileForm: newProfileForm, }); }, - _onShortDescChange: function(e) { - const newProfileForm = Object.assign(this.state.profileForm, { short_description: e.target.value }); + _onShortDescChange: function(value) { + const newProfileForm = Object.assign(this.state.profileForm, { short_description: value }); this.setState({ profileForm: newProfileForm, }); @@ -878,18 +878,28 @@ export default React.createClass({ ); - nameNode = ; - shortDescNode = ; + + const EditableText = sdk.getComponent("elements.EditableText"); + + nameNode = ; + + shortDescNode = ; rightButtons.push( ; if (summary.profile && summary.profile.name) { - nameNode =
+ nameNode =
{ summary.profile.name } ({ this.props.groupId })
; } else { - nameNode = { this.props.groupId }; + nameNode = { this.props.groupId }; } if (summary.profile && summary.profile.short_description) { - shortDescNode = { summary.profile.short_description }; + shortDescNode = { summary.profile.short_description }; } rightButtons.push(