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 =