Permalink
Browse files

Merge pull request #28 from jdavid/master

Internationalization
  • Loading branch information...
tamasd committed Aug 30, 2017
2 parents 4bc99b1 + 649f2fc commit dcd304447470c423a5f92c347184248f37418030
View
@@ -5,3 +5,5 @@ assets/*
dist
public/*
test.toml
.*.swp
View
@@ -38,3 +38,9 @@ docker: preparedocker
rebuildsearch:
go build -o wh-rebuildsearch cmd/wh-rebuildsearch/main.go
gettext:
python gettext.py
msgmerge -U locales/en.po locales/messages.pot
msgmerge -U locales/fr.po locales/messages.pot
npm run stonejs -- build --merge locales/*.po js/messages.json
View
@@ -59,6 +59,7 @@ If unsure, leave them empty.
* `googleanalyticsaccount`: enables Google Analytics.
* `mailchimp`: mailchimp-related configuration. If set, new users will be added to a mailing list.
* `metricsaddresses`: comma separated whitelist of addresses from where the `/metrics` endpoint is accessible
* `languages`: list of languages for the user interface
## Build & Run
View
@@ -36,5 +36,6 @@
"listid": "",
"datacenter": ""
},
"googleanalyticsaccount": ""
"googleanalyticsaccount": "",
"languages": ["en"]
}
View
@@ -0,0 +1,46 @@
import os
import re
HEADER = r"""# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-20 17:40+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
"""
if __name__ == '__main__':
rexp = re.compile('(.*?[^a-zA-Z0-9_]|^)(t|N_)\("(.*?)"[\)|,]')
pot = open('locales/messages.pot', 'w')
pot.write(HEADER)
msgs = {}
for root, dirs, files in os.walk('js'):
for filename in files:
if filename.endswith('.js'):
filename = os.path.join(root, filename)
for line_no, line in enumerate(open(filename)):
source = '%s:%d' % (filename, line_no + 1)
for match in rexp.finditer(line):
msgs.setdefault(match.group(3), []).append(source)
for msg in sorted(msgs):
pot.write('\n')
for source in sorted(msgs[msg]):
pot.write('#: %s\n' % source)
pot.write('msgid "{}"\nmsgstr ""\n'.format(msg))
@@ -16,7 +16,7 @@
import $ from "jquery";
import {capitalizeFirstLetter, split} from "util";
import {t} from "t";
import {t, N_} from "t";
import Translator from "client/walkthrough/translator";
class AutoTitle {
@@ -25,8 +25,8 @@ class AutoTitle {
const [sentence, paragraph, params] = this.locatorToSentence(command, locator);
const verb = this.commandToVerb(command);
params["@verb"] = t(verb);
params["@value"] = value;
params["verb"] = t(verb);
params["value"] = value;
return [t(sentence, params), t(paragraph, params)];
}
@@ -47,10 +47,10 @@ class AutoTitle {
linkSentenceGenerator(command, prefix, argument) {
return [
"@verb on @argument",
N_("{verb} on {argument}"),
"",
{
"@argument": argument,
"argument": argument,
},
];
}
@@ -59,10 +59,10 @@ class AutoTitle {
const elem = Translator.instance().translate(prefix+"="+argument);
const label = this.getLabel(elem);
return [
"@verb in @label",
N_("{verb} in {label}"),
"",
{
"@label": label,
"label": label,
},
];
}
@@ -20,6 +20,7 @@ import {MAXIMUM_ZINDEX} from "util";
import editDialog from "client/walkthrough/editdialog";
import EventAbsorber from "client/walkthrough/eventabsorber";
import LocatorGenerator from "client/walkthrough/locator_generator";
import {t} from "t";
class Bubble {
@@ -111,7 +112,7 @@ class Bubble {
this.nextButton = $("<a />")
.attr("href", "#")
.text("Next")
.text(t("Next"))
.addClass("wtbubble-button")
.addClass("wtbubble-next")
.addClass("wtbubble-button")
@@ -130,7 +131,7 @@ class Bubble {
.attr("href", "#")
.addClass("wtbubble-edit")
.addClass("wtbubble-button")
.text("Edit")
.text(t("Edit"))
.click(function (event) {
event.preventDefault();
that.editdialog = new editDialog(that.step, that.contentWrapper);
@@ -16,6 +16,7 @@
import $ from "jquery";
import Bubble from "client/walkthrough/bubble";
import {t} from "t";
class editDialog {
@@ -217,7 +218,7 @@ class editDialog {
this.controller.client.getSuggestions(this.step.cmd, this.step.arg0, this.step.arg1, function (data) {
suggestionwrapper
.show()
.append($("<p/>").text("Suggestions: "));
.append($("<p/>").text(t("Suggestions: ")));
for (var i in data) {
if (data.hasOwnProperty(i)) {
$("<p />")
@@ -24,7 +24,7 @@ import Controller from "client/walkthrough/controller";
import Bubble from "client/walkthrough/bubble";
import CommandDispatcher from "client/walkthrough/command_dispatcher";
import Translator from "client/walkthrough/translator";
import {t} from "t";
import {t, N_} from "t";
import URI from "URIjs";
import {getdata} from "util";
@@ -130,13 +130,13 @@ class Executor {
},
waiting: (tries, remainingtries) => {
const message = step.canEdit ?
"The @number. bubble is not found. Go to the !editlink form to repair it. Technical info: @locator" :
"The @number. bubble is not found. Report it to the owner.";
N_("The {number}. bubble is not found. Go to the {editlink} form to repair it. Technical info: {locator}") :
N_("The {number}. bubble is not found. Report it to the owner.");
if ((tries-remainingtries) > 10) {
this.client.showError("locator-fail", t(message, {
"@number": this.controller.state.stepIndex + 1,
"@locator": step.highlight,
"!editlink": `<a href="/walkthrough/${this.controller.state.walkthrough}" target="_top">edit walkthrough</a>`, // TODO replace this with a proper router generated link
"number": this.controller.state.stepIndex + 1,
"locator": step.highlight,
"editlink": `<a href="/walkthrough/${this.controller.state.walkthrough}" target="_top">edit walkthrough</a>`, // TODO replace this with a proper router generated link
}));
}
error = true;
@@ -19,6 +19,7 @@ import EventAbsorber from "client/walkthrough/eventabsorber";
import Util from "client/walkthrough/util";
import LocatorGenerator from "client/walkthrough/locator_generator";
import AutoTitle from "client/walkthrough/autotitle";
import {N_} from "t";
class Recorder {
@@ -107,7 +108,7 @@ class Recorder {
if (!Util.isInputElement(clickedElement)) {
const locator = LocatorGenerator.instance().generate(clickedElement);
const [title, description] = AutoTitle.instance().titleAndDescription("click", locator);
const [title, description] = AutoTitle.instance().titleAndDescription("click", locator); N_("Click");
this.client.saveStep("click", locator, null, title, description);
this.animateRecordedElement(clickedElement);
}
@@ -149,7 +150,7 @@ class Recorder {
let title, description;
switch (tagName) {
case "select":
[title, description] = AutoTitle.instance().titleAndDescription("select", locator, value);
[title, description] = AutoTitle.instance().titleAndDescription("select", locator, value); N_("Select");
this.client.saveStep("select", locator, "value=" + value, title, description);
this.animateRecordedElement(element);
break;
@@ -160,7 +161,7 @@ class Recorder {
this.client.enablePasswordParameter();
}
const value = ispw ? "[password]" : value;
[title, description] = AutoTitle.instance().titleAndDescription("type", locator, value);
[title, description] = AutoTitle.instance().titleAndDescription("type", locator, value); N_("Type");
this.client.saveStep("type", locator, value);
this.animateRecordedElement(element);
break;
@@ -169,7 +170,7 @@ class Recorder {
var valueDom = $(element.html());
valueDom.find(".walkthrough-eventabsorber-hover").removeClass("walkthrough-eventabsorber-hover");
var finalValue = $("<div />").append(valueDom).html();
[title, description] = AutoTitle.instance().titleAndDescription("type", locator, finalValue);
[title, description] = AutoTitle.instance().titleAndDescription("type", locator, finalValue); N_("Type");
this.client.saveStep("type", locator, finalValue, title, description);
this.animateRecordedElement(element);
} else {
View
@@ -55,13 +55,13 @@ class Connect extends React.Component {
const url = `/api/auth/${provider.id}/connect?token=${csrfToken}`;
return (
<div key={provider.id} className={"col-xs-10 col-xs-offset-1 provider-"+provider.id}>
<a href={url} className="btn btn-primary btn-block">{t("Log in with @label", {"@label": provider.label})}</a>
<a href={url} className="btn btn-primary btn-block">{t("Log in with {label}", {"label": provider.label})}</a>
</div>
);
});
const signupButton = (!DISABLE_REGISTRATION && this.props.password && !this.props.signup) ? (
<span>New to WalkHub? <a href="#" className="register-link" onClick={this.props.signupClick}>{t("Register")}</a></span>
<span>{t("New to WalkHub?")} <a href="#" className="register-link" onClick={this.props.signupClick}>{t("Register")}</a></span>
) : null;
let signinForm = null;
@@ -17,7 +17,7 @@
import React from "react";
import EmbedCode from "components/embedcode";
import {noop} from "form";
import {t} from "t";
import {t, N_} from "t";
import URI from "URIjs";
class EmbedCodeBuilder extends React.Component {
@@ -47,6 +47,7 @@ class EmbedCodeBuilder extends React.Component {
let advancedSettings = null;
if (this.props.enableAdvanced && !this.props.showCode) {
if (this.props.showAdvanced) {
N_("none"); N_("bottom right"); N_("bottom left"); N_("top right"), N_("top left");
const positions = ["none", "bottom-right", "bottom-left", "top-right", "top-left"].map((position) => {
const label = t(position.replace("-", " "));
return (
@@ -124,7 +124,7 @@ class ProfileEdit extends React.Component {
return (
<div key={provider.id} className="row">
<div className="col-xs-6">
{t("%label log in", {"%label": provider.label})}
{t("{label} log in", {"label": provider.label})}
</div>
<div className="col-xs-6">
<a href={url} className="btn btn-default btn-sm profile-edit-button">{t("Connect")}</a>
@@ -21,7 +21,7 @@ import UserActions from "actions/user";
import connectToStores from "alt/utils/connectToStores";
import flux from "control";
import {noop} from "form";
import {t} from "t";
import {t, N_} from "t";
import {capitalizeFirstLetter} from "util";
import RouterActions from "actions/router";
import OuterClassActions from "actions/outerclass";
@@ -45,12 +45,12 @@ if (WALKHUB_ANNOUNCEMENT) {
let menuItems = {
navbar: {
left: [
{path: "https://github.com/Pronovix/walkhub-service", label: "Download from GitHub"},
{path: "https://github.com/Pronovix/walkhub-service", label: N_("Download from GitHub")},
],
right: [
{path: "/search", label: "Search"},
{path: "/profile/me", label: "My Profile", loggedin: true},
{path: "/record", label: "Record", loggedin: true},
{path: "/search", label: N_("Search")},
{path: "/profile/me", label: N_("My Profile"), loggedin: true},
{path: "/record", label: N_("Record"), loggedin: true},
{path: "/connect", icon: "log-in", loggedin: false},
{path: "/api/auth/logout?token=CSRF_TOKEN", icon: "log-out", loggedin: true},
],
@@ -182,8 +182,8 @@ class RecordWrapper extends React.Component {
noop(evt);
const title = this.state.title ?
this.state.title :
t("Walkthrough on @domain", {
"@domain": URI(this.state.startingUrl).hostname(),
t("Walkthrough on {domain}", {
"domain": URI(this.state.startingUrl).hostname(),
});
const runner = this.getRunner();
if (runner.getName() === "popup") {
Oops, something went wrong.

0 comments on commit dcd3044

Please sign in to comment.