Merge pull request #1158 from Pita/feature/i18n

Internationalization support!  Yay!
This commit is contained in:
Marcel Klehr 2012-11-14 09:06:56 -08:00
commit 6426b05c67
17 changed files with 1608 additions and 124 deletions

View file

@ -52,3 +52,11 @@ Default: false
* Boolean
Default: false
## lang
* String
Default: en
Example: `lang=ar` (translates the interface into Arabic)

View file

@ -1,5 +1,6 @@
@include documentation
@include cusotm_static
@include localization
@include custom_static
@include api/api
@include plugins
@include database

19
doc/localization.md Normal file
View file

@ -0,0 +1,19 @@
# Localization
Etherpad lite provides a multi-language user interface, that's apart from your users' content, so users from different countries can collaborate on a single document, while still having the user interface displayed in their mother tongue.
## Translating
`/src/locales` contains files for all supported languages which contain the translated strings. To add support for a new language, copy the English language file named `en.ini` and translate it.
Translation files are simply `*.ini` files and look like this:
```
pad.modals.connected = Connecté.
pad.modals.uderdup = Ouvrir dans une nouvelle fenêtre.
pad.toolbar.unindent.title = Désindenter
pad.toolbar.undo.title = Annuler (Ctrl-Z)
timeslider.pageTitle = {{appTitle}} Curseur temporel
```
There must be only one translation per line. Each translation consists of a key (the id of the string that is to be translated), an equal sign and the translated string. Anything after the equa sign will be used as the translated string (you may put some spaces after `=` for better readability, though). Terms in curly braces must not be touched but left as they are, since they represent a dynamically changing part of the string like a variable. Imagine a message welcoming a user: `Welcome, {{userName}}!` would be translated as `Ahoy, {{userName}}!` in pirate.
## Under the hood
We use a `language` cookie to save your language settings if you change them. If you don't, we autodetect your locale using information from your browser. Now, that we know your preferred language this information is feeded into a very nice library called [webL10n](https://github.com/fabi1cazenave/webL10n), which loads the appropriate translations and applies them to our templates, providing translation params, pluralization, include rules and even a nice javascript API along the way.

View file

@ -5,6 +5,7 @@
"restartServer": "ep_etherpad-lite/node/hooks/express:restartServer"
} },
{ "name": "static", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/static:expressCreateServer" } },
{ "name": "i18n", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/i18n:expressCreateServer" } },
{ "name": "specialpages", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/specialpages:expressCreateServer" } },
{ "name": "padurlsanitize", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/padurlsanitize:expressCreateServer" } },
{ "name": "padreadonly", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/padreadonly:expressCreateServer" } },

85
src/locales/de.ini Normal file
View file

@ -0,0 +1,85 @@
[de]
index.newPad = Neues Pad
index.createOpenPad = Pad mit folgendem Namen öffnen
pad.toolbar.bold.title = Fett (Strg-B)
pad.toolbar.italic.title = Kursiv (Strg-I)
pad.toolbar.underline.title = Unterstrichen (Strg-U)
pad.toolbar.strikethrough.title = Durchgestrichen
pad.toolbar.ol.title = Nummerierte Liste
pad.toolbar.ul.title = Ungeordnete Liste
pad.toolbar.indent.title = Einrücken
pad.toolbar.unindent.title = Ausrücken
pad.toolbar.undo.title = Rückgängig (Strg-Z)
pad.toolbar.redo.title = Wiederholen (Strg-Y)
pad.toolbar.clearAuthorship.title = Autorenfarben zurücksetzen
pad.toolbar.import_export.title = Import/Export von verschiedenen Dateiformaten
pad.toolbar.timeslider.title = Pad-Geschichte anzeigen
pad.toolbar.savedRevision.title = Diese Revision markieren
pad.toolbar.settings.title = Einstellungen
pad.toolbar.embed.title = Dieses Pad teilen oder einbetten
pad.toolbar.showusers.title = Verbundene Benutzer anzeigen
pad.colorpicker.save = Speichern
pad.colorpicker.cancel = Abbrechen
pad.loading = Laden...
pad.settings.padSettings = Pad Einstellungen
pad.settings.myView = Eigene Ansicht
pad.settings.stickychat = Chat immer anzeigen
pad.settings.colorcheck = Autorenfarben anzeigen
pad.settings.linenocheck = Zeilennummern
pad.settings.fontType = Schriftart:
pad.settings.fontType.normal = Normal
pad.settings.fontType.monospaced = Monospace
pad.settings.language = Sprache:
pad.settings.globalView = Gemeinsame Ansicht
pad.importExport.import_export = Import/Export
pad.importExport.import = Datei oder Dokument hochladen
pad.importExport.successful = Erfolgreich!
pad.importExport.export = Dieses Pad exportieren
pad.importExport.exporthtml = HTML
pad.importExport.exportplain = Reiner Text
pad.importExport.exportword = Microsoft Word
pad.importExport.exportpdf = PDf
pad.importExport.exportopen = ODF (Open Document Format)
pad.importExport.exportdokuwiki = DokuWiki
pad.modals.connected = Verbunden.
pad.modals.reconnecting = Wiederherstellen der Verbindung...
pad.modals.forcereconnect = Erneut Verbinden
pad.modals.uderdup = In einem anderen Fenster geöffnet
pad.modals.userdup.explanation = Dieses Pad scheint in mehr als einem Browser-Fenster auf diesem Computer geöffnet zu sein.
pad.modals.userdup.advice = Um dieses Fenster zu benutzen, verbinden Sie bitte erneut.
pad.modals.unauth = Nicht Authorisiert.
pad.modals.unauth.explanation = Ihre Befugnisse auf dieses Pad zuzugreifen haben sich geädert. Versuchen Sie, erneut zu verbinden.
pad.modals.looping = Verbindung unterbrochen.
pad.modals.looping.explanation = Es gibt Probleme bei der Kommunikation mit dem Synchronisationsserver.
pad.modals.looping.cause = Möglicherweise verläuft Ihre Verbindung durch eine inkompatible Firewall oder einen inkompatiblen Proxy.
pad.modals.initsocketfail = Server nicht erreichbar.
pad.modals.initsocketfail.explanation = Es konnte keine Verbindung zum Synchronisationsserver hergestellt werden.
pad.modals.initsocketfail.cause = Dies könnte an Ihrem Browser oder Ihrer Internet-Verbindung liegen.
pad.modals.slowcommit = Verbindung unterbrochen.
pad.modals.slowcommit.explanation = Der Server reagiert nicht.
pad.modals.slowcommit.cause = Dies könnte an Problemen mit Netzwerk-Konnektivität liegen. Möglicherweise ist der Server aber auch überlastet.
pad.modals.deleted = Entfernt.
pad.modals.deleted.explanation = Dieses Pad wurde entfernt.
pad.modals.disconnected = Verbindung unterbrochen.
pad.modals.disconnected.explanation = Die Verbindung zum Synchronisationsserver wurde unterbrochen.
pad.modals.disconnected.cause = Möglicherweise ist der Server nicht erreichbar. Bitte benachrichtigen Sie uns, falls dies weiterhin passiert.
pad.share = Dieses Pad teilen
pad.share.readonly = Eingeschränkter zugriff (Nur lesen)
pad.share.link = Link
pad.share.emebdcode = In Webseite einbetten
pad.chat = Chat
pad.chat.title = Den Chat für dieses Pad öffnen
timeslider.pageTitle = {{appTitle}} Pad-Geschichte
timeslider.toolbar.returnbutton = Zurück zum Pad
timeslider.toolbar.authors = Autoren:
timeslider.toolbar.authorsList = keine Autoren
timeslider.exportCurrent = Exportiere diese Version als:

77
src/locales/en.ini Normal file
View file

@ -0,0 +1,77 @@
[en]
index.newPad = New Pad
index.createOpenPad = or create/open a Pad with the name:
pad.toolbar.bold.title = Bold (Ctrl-B)
pad.toolbar.italic.title = Italic (Ctrl-I)
pad.toolbar.underline.title = Underline (Ctrl-U)
pad.toolbar.strikethrough.title = Strikethrough
pad.toolbar.ol.title = Ordered list
pad.toolbar.ul.title = UnOrdered List
pad.toolbar.indent.title = Indent
pad.toolbar.unindent.title = Outdent
pad.toolbar.undo.title = Undo (Ctrl-Z)
pad.toolbar.redo.title = Redo (Ctrl-Y)
pad.toolbar.clearAuthorship.title = Clear Authorship Colors
pad.toolbar.import_export.title = Import/Export from/to different file formats
pad.toolbar.timeslider.title = Timeslider
pad.toolbar.savedRevision.title = Saved Revisions
pad.toolbar.settings.title = Settings
pad.toolbar.embed.title = Embed this pad
pad.toolbar.showusers.title = Show the users on this pad
pad.colorpicker.save = Save
pad.colorpicker.cancel = Cancel
pad.loading = Loading...
pad.settings.padSettings = Pad Settings
pad.settings.myView = My View
pad.settings.stickychat = Chat always on screen
pad.settings.colorcheck = Authorship colors
pad.settings.linenocheck = Line numbers
pad.settings.fontType = Font type:
pad.settings.fontType.normal = Normal
pad.settings.fontType.monospaced = Monospace
pad.settings.globalView = Global View
pad.settings.language = Language:
pad.importExport.import_export = Import/Export
pad.importExport.import = Upload any text file or document
pad.importExport.successful = Successful!
pad.importExport.export = Export current pad as
pad.importExport.exporthtml = HTML
pad.importExport.exportplain = Plain text
pad.importExport.exportword = Microsoft Word
pad.importExport.exportpdf = PDf
pad.importExport.exportopen = ODF (Open Document Format)
pad.importExport.exportdokuwiki = DokuWiki
pad.modals.connected = Connected.
pad.modals.reconnecting = Reconnecting to your pad..
pad.modals.forcereconnect = Force reconnect
pad.modals.uderdup = Open in another window
pad.modals.userdup.explanation = This pad seems to be opened in more than one browser window on this computer.
pad.modals.userdup.advice = Reconnect to use this windows instead.
pad.modals.unauth = Not authorized
pad.modals.unauth.explanation = Your permissions have changes while viewing this page. Try to reconnect.
pad.modals.looping = Disconnected.
pad.modals.looping.explanation = We're having problem communicating to the synchronization server.
pad.modals.looping.cause = Perhaps their connection runs through an incompatible firewall or incompatible proxy.
pad.modals.initsocketfail = Server is unreachable.
pad.modals.initsocketfail.explanation = Couldn't connect to the synchronization server.
pad.modals.initsocketfail.cause = This could be because of your browser or Internet connection. #sounds stupid!
pad.modals.slowcommit = Disconnected.
pad.modals.slowcommit.explanation = The server is not responding.
pad.modals.slowcommit.cause = This could be due to problems with network connectivity.
pad.modals.deleted = Deleted.
pad.modals.deleted.explanation = This pad has been removed.
pad.modals.disconnected = You have been disconnected.
pad.modals.disconnected.explanation = The connection to the server was lost
pad.modals.disconnected.cause = The server may be unavailable. Please notify us if this continues to happen.
pad.share = Share this pad
pad.share.readonly = Read only
pad.share.link = Link
pad.share.emebdcode = Embed URL
pad.chat = Chat
pad.chat.title = Open the chat for this pad.
timeslider.pageTitle = {{appTitle}} Timeslider
timeslider.toolbar.returnbutton = Return to pad
timeslider.toolbar.authors = Authors:
timeslider.toolbar.authorsList = No Authors
timeslider.exportCurrent = Export current version as:

86
src/locales/fr.ini Normal file
View file

@ -0,0 +1,86 @@
[fr]
index.newPad = Nouveau Pad
index.createOpenPad = ou créer/ouvrir un Pad intitulé
pad.toolbar.bold.title = Gras (Ctrl-B)
pad.toolbar.italic.title = Italique (Ctrl-I)
pad.toolbar.underline.title = Souligner (Ctrl-U)
pad.toolbar.strikethrough.title = Barrer
pad.toolbar.ol.title = Liste ordonnée
pad.toolbar.ul.title = Liste non-ordonnée
pad.toolbar.indent.title = Indenter
pad.toolbar.unindent.title = Désindenter
pad.toolbar.undo.title = Annuler (Ctrl-Z)
pad.toolbar.redo.title = Rétablir (Ctrl-Y)
pad.toolbar.clearAuthorship.title = Effacer les couleurs identifant les auteurs
pad.toolbar.import_export.title = Importer/Exporter de/vers un format de fichier différent
pad.toolbar.timeslider.title = Navigateur d'historique
pad.toolbar.savedRevision.title = Versions enregistrées
pad.toolbar.settings.title = Paramètres
pad.toolbar.embed.title = Intégrer ce Pad
pad.toolbar.showusers.title = Afficher les utilisateurs du Pad
pad.colorpicker.save = Sauver
pad.colorpicker.cancel = Annuler
pad.loading = Chargement...
pad.settings.padSettings = Paramètres du Pad
pad.settings.myView = Ma vue
pad.settings.stickychat = Messagerie toujours affichée
pad.settings.colorcheck = Couleurs d'identification
pad.settings.linenocheck = Numéros des lignes
pad.settings.fontType = Type de police:
pad.settings.fontType.normal = Normal
pad.settings.fontType.monospaced = Monospace
pad.settings.globalView = Vue d'ensemble
pad.settings.language = Langue:
pad.importExport.import_export = Importer/Exporter
pad.importExport.import = Charger un texte ou un document
pad.importExport.successful = Traitement effectué!
pad.importExport.export = Exporter ce Pad vers
pad.importExport.exporthtml = HTML
pad.importExport.exportplain = Texte brut
pad.importExport.exportword = Microsoft Word
pad.importExport.exportpdf = PDf
pad.importExport.exportopen = ODF (Open Document Format)
pad.importExport.exportdokuwiki = DokuWiki
pad.modals.connected = Connecté.
pad.modals.reconnecting = Reconnexion vers votre Pad...
pad.modals.forcereconnect = Forcer la reconnexion.
pad.modals.uderdup = Ouvrir dans une nouvelle fenêtre
pad.modals.userdup.explanation = Ce Pad semble avoir été ouvert dans plusieurs fenêtres de votre fureteur sur cet ordinateur.
pad.modals.userdup.advice = Se reconnecter en utilisant cette fenêtre.
pad.modals.unauth = Not authorized Non authorisé
pad.modals.unauth.explanation = Vos permissions ont été changées lors de la visualisation de cette page. Essayer de vous reconnecter.
pad.modals.looping = Disconnected. Déconnecté.
pad.modals.looping.explanation = Nous éprouvons un problème de communication au serveur de synchronisation.
pad.modals.looping.cause = Il est possible que leur connection soit protégée par un pare-feu incompatible ou un serveur proxy incompatible.
pad.modals.initsocketfail = Le serveur est introuvable.
pad.modals.initsocketfail.explanation = Impossible de se connecter au serveur de synchronisation.
pad.modals.initsocketfail.cause = La cause de ce problème peut être liée à votre fureteur web.
pad.modals.slowcommit = Disconnected. Déconnecté
pad.modals.slowcommit.explanation = Le serveur ne répond pas.
pad.modals.slowcommit.cause = La cause de ce problème peut être liée à une erreur de connectivité du réseau.
pad.modals.deleted = Supprimé.
pad.modals.deleted.explanation = Ce Pad a été supprimé.
pad.modals.disconnected = Vous avez été déconnecté.
pad.modals.disconnected.explanation = La connexion au serveur a échoué.
pad.modals.disconnected.cause = Ce serveur est possiblement hors-ligne. Veuillez nous joindre si le problème persiste.
pad.share = Partager ce Pad
pad.share.readonly = Lecture seule
pad.share.link = Lien
pad.share.emebdcode = Lien à intégrer
pad.chat = Messagerie
pad.chat.title = Ouvrir la messagerie liée au Pad.
timeslider.pageTitle = {{appTitle}} Curseur temporel
timeslider.toolbar.returnbutton = Retour à ce Pad.
timeslider.toolbar.authors = Auteurs:
timeslider.toolbar.authorsList = Aucun auteurs
timeslider.exportCurrent = Exporter version actuelle vers:

33
src/node/hooks/i18n.js Normal file
View file

@ -0,0 +1,33 @@
var Globalize = require('globalize')
, fs = require('fs')
, path = require('path')
, express = require('express')
var localesPath = __dirname+"/../../locales";
var localeIndex = '[*]\r\n@import url(locales/en.ini)\r\n';
exports.availableLangs = {en: 'English'};
fs.readdir(localesPath, function(er, files) {
files.forEach(function(locale) {
locale = locale.split('.')[0]
if(locale.toLowerCase() == 'en') return;
// build locale index
localeIndex += '['+locale+']\r\n@import url(locales/'+locale+'.ini)\r\n'
require('globalize/lib/cultures/globalize.culture.'+locale+'.js')
var culture = Globalize.cultures[locale];
exports.availableLangs[culture.name] = culture.nativeName;
})
})
exports.expressCreateServer = function(n, args) {
args.app.use('/locales', express.static(localesPath));
args.app.get('/locales.ini', function(req, res) {
res.send(localeIndex);
})
}

View file

@ -35,7 +35,8 @@
"security" : "1.0.0",
"tinycon" : "0.0.1",
"underscore" : "1.3.1",
"unorm" : "1.0.0"
"unorm" : "1.0.0",
"globalize" : "0.1.1"
},
"bin": { "etherpad-lite": "./node/server.js" },
"devDependencies": {

View file

@ -748,6 +748,15 @@ input[type=checkbox] {
.popup p {
margin: 5px 0
}
.popup select {
background: #fff;
padding: 2px;
height: 24px;
border-radius: 3px;
border: 1px solid #ccc;
outline: none;
min-width: 105px;
}
.column {
float: left;
width: 50%;

1028
src/static/js/l10n.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -111,6 +111,7 @@ function getParams()
var IsnoColors = params["noColors"];
var rtl = params["rtl"];
var alwaysShowChat = params["alwaysShowChat"];
var lang = params["lang"];
if(IsnoColors)
{
@ -173,6 +174,13 @@ function getParams()
chat.stickToScreen();
}
}
if(lang)
{
if(lang !== "")
{
document.webL10n.setLanguage(lang);
}
}
}
function getUrlVars()
@ -451,6 +459,7 @@ var pad = {
{
pad.collabClient.sendClientMessage(msg);
},
createCookie: createCookie,
init: function()
{

View file

@ -75,6 +75,11 @@ var padeditor = (function()
{
pad.changeViewOption('useMonospaceFont', $("#viewfontmenu").val() == 'monospace');
});
$("#languagemenu").val(document.webL10n.getLanguage());
$("#languagemenu").change(function() {
pad.createCookie("language",$("#languagemenu").val(),null,'/');
document.webL10n.setLanguage($("#languagemenu").val());
});
},
setViewOptions: function(newOptions)
{

View file

@ -31,7 +31,8 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<script type="text/javascript" src="static/js/l10n.js"></script>
<link rel="resource" type="application/l10n" href="locales.ini" />
<link rel="shortcut icon" href="<%=settings.favicon%>">
<style>
@ -148,8 +149,8 @@
<div id="wrapper">
<div id="inner">
<div id="button" onclick="go2Random()" class="translate">New Pad</div>
<div id="label" class="translate">or create/open a Pad with the name</div>
<div id="button" onclick="go2Random()" data-l10n-id="index.newPad"></div>
<div id="label" data-l10n-id="index.createOpenPad"></div>
<form action="#" onsubmit="go2Name();return false;">
<input type="text" id="padname" autofocus x-webkit-speech>
<button type="submit">OK</button>
@ -184,6 +185,12 @@
return randomstring;
}
(function(document) {
// Set language for l10n
var language = document.cookie.match(/language=(\w{2})/);
document.documentElement.lang = language? language[1] : 'en';
})(document)
// start the custom js
if (typeof customStart == "function") customStart();
</script>

View file

@ -1,5 +1,6 @@
<%
var settings = require("ep_etherpad-lite/node/utils/Settings");
var settings = require("ep_etherpad-lite/node/utils/Settings")
, langs = require("ep_etherpad-lite/node/hooks/i18n").availableLangs
%>
<!doctype html>
<html>
@ -32,6 +33,10 @@
<meta name="robots" content="noindex, nofollow">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<script type="text/javascript" src="../static/js/l10n.js"></script>
<link rel="resource" type="application/l10n" href="../locales.ini" />
<link rel="shortcut icon" href="<%=settings.favicon%>">
<% e.begin_block("styles"); %>
@ -50,60 +55,60 @@
<ul class="menu_left">
<% e.begin_block("editbarMenuLeft"); %>
<li class="acl-write" id="bold" data-key="bold">
<a class="grouped-left" title="Bold (ctrl-B)">
<a class="grouped-left" data-l10n-id="pad.toolbar.bold">
<span class="buttonicon buttonicon-bold"></span>
</a>
</li>
<li class="acl-write" id="italic" data-key="italic">
<a class="grouped-middle" title="Italics (ctrl-I)">
<a class="grouped-middle" data-l10n-id="pad.toolbar.italic">
<span class="buttonicon buttonicon-italic"></span>
</a>
</li>
<li class="acl-write" id="underline" data-key="underline">
<a class="grouped-middle" title="Underline (ctrl-U)">
<a class="grouped-middle" data-l10n-id="pad.toolbar.underline">
<span class="buttonicon buttonicon-underline"></span>
</a>
</li>
<li class="acl-write" id="strikethrough" data-key="strikethrough">
<a class="grouped-right" title="Strikethrough">
<a class="grouped-right" data-l10n-id="pad.toolbar.strikethrough">
<span class="buttonicon buttonicon-strikethrough"></span>
</a>
</li>
<li class="acl-write separator"></li>
<li class="acl-write" id="oderedlist" data-key="insertorderedlist">
<a class="grouped-left" title="Toggle Ordered List">
<a class="grouped-left" data-l10n-id="pad.toolbar.ol">
<span class="buttonicon buttonicon-insertorderedlist"></span>
</a>
</li>
<li class="acl-write" id="unoderedlist" data-key="insertunorderedlist">
<a class="grouped-middle" title="Toggle Bullet List">
<a class="grouped-middle" data-l10n-id="pad.toolbar.ul">
<span class="buttonicon buttonicon-insertunorderedlist"></span>
</a>
</li>
<li class="acl-write" id="indent" data-key="indent">
<a class="grouped-middle" title="Indent">
<a class="grouped-middle" data-l10n-id="pad.toolbar.indent">
<span class="buttonicon buttonicon-indent"></span>
</a>
</li>
<li class="acl-write" id="outdent" data-key="outdent">
<a class="grouped-right" title="Unindent">
<a class="grouped-right" data-l10n-id="pad.toolbar.unindent">
<span class="buttonicon buttonicon-outdent"></span>
</a>
</li>
<li class="acl-write separator"></li>
<li class="acl-write" id="undo" data-key="undo">
<a class="grouped-left" title="Undo (ctrl-Z)">
<a class="grouped-left" data-l10n-id="pad.toolbar.undo">
<span class="buttonicon buttonicon-undo"></span>
</a>
</li>
<li class="acl-write" id="redo" data-key="redo">
<a class="grouped-right" title="Redo (ctrl-Y)">
<a class="grouped-right" data-l10n-id="pad.toolbar.redo">
<span class="buttonicon buttonicon-redo"></span>
</a>
</li>
<li class="acl-write separator"></li>
<li class="acl-write" id="clearAuthorship" data-key="clearauthorship">
<a title="Clear Authorship Colors">
<a data-l10n-id="pad.toolbar.clearAuthorship">
<span class="buttonicon buttonicon-clearauthorship"></span>
</a>
</li>
@ -112,34 +117,34 @@
<ul class="menu_right">
<% e.begin_block("editbarMenuRight"); %>
<li data-key="import_export">
<a class="grouped-left" id="importexportlink" title="Import/Export from/to different document formats">
<a class="grouped-left" id="importexportlink" data-l10n-id="pad.toolbar.import_export">
<span class="buttonicon buttonicon-import_export"></span>
</a>
</li>
<li onClick="document.location = document.location.pathname+ '/timeslider'">
<a id="timesliderlink" class="grouped-middle" title="Show the history of this pad">
<a id="timesliderlink" class="grouped-middle" data-l10n-id="pad.toolbar.timeslider">
<span class="buttonicon buttonicon-history"></span>
</a>
</li>
<li class="acl-write" data-key="savedRevision">
<a class="grouped-right" id="revisionlink" title="Mark this revision as a saved revision">
<a class="grouped-right" id="revisionlink" data-l10n-id="pad.toolbar.savedRevision">
<span class="buttonicon buttonicon-savedRevision"></span>
</a>
</li>
<li class="acl-write separator"></li>
<li class="acl-write" data-key="settings">
<a class="grouped-left" id="settingslink" title="Settings of this pad">
<a class="grouped-left" id="settingslink" data-l10n-id="pad.toolbar.settings">
<span class="buttonicon buttonicon-settings"></span>
</a>
</li>
<li data-key="embed">
<a class="grouped-right" id="embedlink" title="Share and Embed this pad">
<a class="grouped-right" id="embedlink" data-l10n-id="pad.toolbar.embed">
<span class="grouped-right buttonicon buttonicon-embed"></span>
</a>
</li>
<li class="separator"></li>
<li id="usericon" data-key="showusers">
<a title="Show connected users">
<a data-l10n-id="pad.toolbar.showusers">
<span class="buttonicon buttonicon-showusers"></span>
<span id="online_count">1</span>
</a>
@ -153,8 +158,8 @@
<div id="myuser">
<div id="mycolorpicker">
<div id="colorpicker"></div>
<button id="mycolorpickersave">Save</button>
<button id="mycolorpickercancel">Cancel</button>
<button id="mycolorpickersave" data-l10n-id="pad.colorpicker.save"></button>
<button id="mycolorpickercancel" data-l10n-id="pad.colorpicker.cancel"></button>
<span id="mycolorpickerpreview" class="myswatchboxhoverable"></span>
</div>
<div id="myswatchbox"><div id="myswatch"></div></div>
@ -174,56 +179,76 @@
<div id="editorcontainerbox">
<div id="editorcontainer"></div>
<div id="editorloadingbox">
<p>Loading...</p>
<p data-l10n-id="pad.loading">Loading...</p>
<noscript><strong>Sorry, you have to enable Javascript in order to use this.</strong></noscript>
</div>
</div>
<div id="settings" class="popup">
<h1>Pad settings</h1>
<h1 data-l10n-id="pad.settings.padSettings"></h1>
<div class="column">
<% e.begin_block("mySettings"); %>
<h2>My view</h2>
<h2 data-l10n-id="pad.settings.myView"></h2>
<p>
<input type="checkbox" id="options-stickychat" onClick="chat.stickToScreen();">
<label for="options-stickychat">Chat always on screen</label>
<label for="options-stickychat" data-l10n-id="pad.settings.stickychat"></label>
</p>
<p>
<input type="checkbox" id="options-colorscheck">
<label for="options-colorscheck">Authorship colors</label>
<label for="options-colorscheck" data-l10n-id="pad.settings.colorcheck"></label>
</p>
<p>
<input type="checkbox" id="options-linenoscheck" checked>
<label for="options-linenoscheck">Line numbers</label>
</p>
<p>
Font type:
<select id="viewfontmenu">
<option value="normal">Normal</option>
<option value="monospace">Monospaced</option>
</select>
<label for="options-linenoscheck" data-l10n-id="pad.settings.linenocheck"></label>
</p>
<% e.end_block(); %>
<table>
<% e.begin_block("mySettings.dropdowns"); %>
<tr>
<td>
<label for="viewfontmenu" data-l10n-id="pad.settings.fontType">Font type:</label>
</td>
<td>
<select id="viewfontmenu">
<option value="normal" data-l10n-id="pad.settings.fontType.normal"></option>
<option value="monospace" data-l10n-id="pad.settings.fontType.monospaced"></option>
</select>
</td>
</tr>
<tr>
<td>
<label for="languagemenu" data-l10n-id="pad.settings.language">Language:</label>
</td>
<td>
<select id="languagemenu">
<% for (lang in langs) { %>
<option value="<%=lang%>"><%=langs[lang]%></option>
<% } %>
</select>
</td>
</tr>
<% e.end_block(); %>
</table>
</div>
<div class="column">
<% e.begin_block("globalSettings"); %>
<h2>Global view</h2>
<h2 data-l10n-id="pad.settings.globalView"></h2>
<% e.end_block(); %>
</div>
</div>
<div id="importexport" class="popup">
<h1>Import/Export</h1>
<h1 data-l10n-id="pad.importExport.import_export"></h1>
<div class="column acl-write">
<% e.begin_block("importColumn"); %>
<h2>Upload any text file or document</h2><br>
<h2 data-l10n-id="pad.importExport.import"></h2><br>
<form id="importform" method="post" action="" target="importiframe" enctype="multipart/form-data">
<div class="importformdiv" id="importformfilediv">
<input type="file" name="file" size="15" id="importfileinput">
<div class="importmessage" id="importmessagefail"></div>
</div>
<div id="import"></div>
<div class="importmessage" id="importmessagesuccess">Successful!</div>
<div class="importmessage" id="importmessagesuccess" data-l10n-id="pad.importExport.successful"></div>
<div class="importformdiv" id="importformsubmitdiv">
<input type="hidden" name="padId" value="blpmaXT35R">
<span class="nowrap">
@ -236,14 +261,14 @@
<% e.end_block(); %>
</div>
<div class="column">
<h2>Export current pad as</h2>
<h2 data-l10n-id="pad.importExport.export"></h2>
<% e.begin_block("exportColumn"); %>
<a id="exporthtmla" target="_blank" class="exportlink"><div class="exporttype" id="exporthtml">HTML</div></a>
<a id="exportplaina" target="_blank" class="exportlink"><div class="exporttype" id="exportplain">Plain text</div></a>
<a id="exportworda" target="_blank" class="exportlink"><div class="exporttype" id="exportword">Microsoft Word</div></a>
<a id="exportpdfa" target="_blank" class="exportlink"><div class="exporttype" id="exportpdf">PDF</div></a>
<a id="exportopena" target="_blank" class="exportlink"><div class="exporttype" id="exportopen">OpenDocument</div></a>
<a id="exportdokuwikia" target="_blank" class="exportlink"><div class="exporttype" id="exportdokuwiki">DokuWiki text</div></a>
<a id="exporthtmla" target="_blank" class="exportlink"><div class="exporttype" id="exporthtml" data-l10n-id="pad.importExport.exporthtml"></div></a>
<a id="exportplaina" target="_blank" class="exportlink"><div class="exporttype" id="exportplain" data-l10n-id="pad.importExport.exportplain"></div></a>
<a id="exportworda" target="_blank" class="exportlink"><div class="exporttype" id="exportword" data-l10n-id="pad.importExport.exportword"></div></a>
<a id="exportpdfa" target="_blank" class="exportlink"><div class="exporttype" id="exportpdf" data-l10n-id="pad.importExport.exportpdf"></div></a>
<a id="exportopena" target="_blank" class="exportlink"><div class="exporttype" id="exportopen" data-l10n-id="pad.importExport.exportopen"></div></a>
<a id="exportdokuwikia" target="_blank" class="exportlink"><div class="exporttype" id="exportdokuwiki" data-l10n-id="pad.importExport.exportdokuwiki"></div></a>
<% e.end_block(); %>
</div>
</div>
@ -251,48 +276,48 @@
<div id="connectivity" class="popup">
<% e.begin_block("modals"); %>
<div class="connected visible">
<h2>Connected.</h2>
<h2 data-l10n-id="pad.modals.connected"></h2>
</div>
<div class="reconnecting">
<h1>Reestablishing connection...</h1>
<h1 data-l10n-id="pad.modals.reconnecting"></h1>
<p><img alt="" border="0" src="../static/img/connectingbar.gif" /></p>
</div>
<div class="userdup">
<h1>Opened in another window.</h1>
<h2>You seem to have opened this pad in another browser window.</h2>
<p>If you'd like to use this window instead, you can reconnect.</p>
<button id="forcereconnect">Reconnect Now</button>
<h1 data-l10n-id="pad.modals.uderdup"></h1>
<h2 data-l10n-id="pad.modals.userdup.explanation"></h2>
<p data-l10n-id="pad.modals.connected.advice"></p>
<button id="forcereconnect" data-l10n-id="pad.modals.forcereconnect"></button>
</div>
<div class="unauth">
<h1>No Authorization.</h1>
<p>Your browser's credentials or permissions have changed while viewing this pad. Try reconnecting.</p>
<button id="forcereconnect">Reconnect Now</button>
<h1 data-l10n-id="pad.modals.unauth"></h1>
<p data-l10n-id="pad.modals.unauth.explanation"></p>
<button id="forcereconnect" data-l10n-id="pad.modals.forcereconnect"></button>
</div>
<div class="looping">
<h1>Disconnected.</h1>
<h2>We're having trouble talking to the EtherPad lite synchronization server.</h2>
<p>You may be connecting through an incompatible firewall or proxy server.</p>
<h1 data-l10n-id="pad.modals.looping"></h1>
<h2 data-l10n-id="pad.modals.looping.explanation"></h2>
<p data-l10n-id="pad.modals.looping.cause"></p>
</div>
<div class="initsocketfail">
<h1>Disconnected.</h1>
<h2>We were unable to connect to the EtherPad lite synchronization server.</h2>
<p>This may be due to an incompatibility with your web browser or internet connection.</p>
<h1 data-l10n-id="pad.modals.initsocketfail"></h1>
<h2 data-l10n-id="pad.modals.initsocketfail.explanation"></h2>
<p data-l10n-id="pad.modals.initsocketfail.cause"></p>
</div>
<div class="slowcommit">
<h1>Disconnected.</h1>
<h2>Server not responding.</h2>
<p>This may be due to network connectivity issues or high load on the server.</p>
<button id="forcereconnect">Reconnect Now</button>
<h1 data-l10n-id="pad.modals.slowcommit"></h1>
<h2 data-l10n-id="pad.modals.slowcommit.explanation"></h2>
<p data-l10n-id="pad.modals.slowcommit.cause"></p>
<button id="forcereconnect" data-l10n-id="pad.modals.forcereconnect"></button>
</div>
<div class="deleted">
<h1>Disconnected.</h1>
<p>This pad was deleted.</p>
<h1 data-l10n-id="pad.modals.deleted"></h1>
<p data-l10n-id="pad.modals.deleted.explanation"></p>
</div>
<div class="disconnected">
<h1>Disconnected.</h1>
<h2>Lost connection with the EtherPad lite synchronization server.</h2>
<p>This may be due to a loss of network connectivity. If this continues to happen, please let us know</p>
<button id="forcereconnect">Reconnect Now</button>
<h1 data-l10n-id="pad.modals.disconnected"></h1>
<h2 data-l10n-id="pad.modals.disconnected.explanation"></h2>
<p data-l10n-id="pad.modals.disconnected.cause"></p>
<button id="forcereconnect" data-l10n-id="pad.modals.forcereconnect"></button>
</div>
<form id="reconnectform" method="post" action="/ep/pad/reconnect" accept-charset="UTF-8" style="display: none;">
<input type="hidden" class="padId" name="padId">
@ -306,16 +331,16 @@
<% e.begin_block("embedPopup"); %>
<div id="embedreadonly" class="right acl-write">
<input type="checkbox" id="readonlyinput">
<label for="readonlyinput">Read only</label>
<label for="readonlyinput" data-l10n-id="pad.share.readonly"></label>
</div>
<h1>Share this pad</h1>
<h1 data-l10n-id="pad.share"></h1>
<div id="linkcode">
<h2>Link</h2>
<h2 data-l10n-id="pad.share.link"></h2>
<input id="linkinput" type="text" value="">
</div>
<br>
<div id="embedcode">
<h2>Embed URL</h2>
<h2 data-l10n-id="pad.share.emebdcode"></h2>
<input id="embedinput" type="text" value="">
</div>
<% e.end_block(); %>
@ -323,14 +348,14 @@
<div id="chatthrob"></div>
<div id="chaticon" title="Open the chat for this pad" onclick="chat.show();return false;">
<span id="chatlabel">Chat</span>
<div id="chaticon" data-l10n-id="pad.chat" onclick="chat.show();return false;">
<span id="chatlabel" data-l10n-id="pad.chat"></span>
<span class="buttonicon buttonicon-chat"></span>
<span id="chatcounter">0</span>
</div>
<div id="chatbox">
<div id="titlebar"><span id ="titlelabel">Chat</span><a id="titlecross" onClick="chat.hide();return false;">-&nbsp;</a></div>
<div id="titlebar"><span id ="titlelabel" data-l10n-id="pad.chat"></span><a id="titlecross" onClick="chat.hide();return false;">-&nbsp;</a></div>
<div id="chattext" class="authorColors"></div>
<div id="chatinputbox">
<form>
@ -374,7 +399,9 @@
<script type="text/javascript">
var clientVars = {};
(function () {
// Set language for l10n
var language = document.cookie.match(/language=(\w{2})/);
document.documentElement.lang = language? language[1] : 'en';
var pathComponents = location.pathname.split('/');

View file

@ -1,9 +1,10 @@
<%
var settings = require("ep_etherpad-lite/node/utils/Settings");
var settings = require("ep_etherpad-lite/node/utils/Settings")
, langs = require("ep_etherpad-lite/node/hooks/i18n").availableLangs
%>
<!doctype html>
<html lang="en">
<title><%=settings.title%> Timeslider</title>
<title data-l10n-id="timeslider.pageTitle" data-l10n-args='{ "appTitle": "<%=settings.title%>" }'><%=settings.title%> Timeslider</title>
<script>
/*
|@licstart The following is the entire license notice for the
@ -31,6 +32,8 @@
<meta charset="utf-8">
<meta name="robots" content="noindex, nofollow">
<link rel="shortcut icon" href="<%=settings.favicon%>">
<script type="text/javascript" src="../../static/js/l10n.js"></script>
<link rel="resource" type="application/l10n" href="../../locales.ini" />
<link rel="stylesheet" href="../../static/css/pad.css">
<link rel="stylesheet" href="../../static/css/timeslider.css">
<link rel="stylesheet" href="../../static/custom/timeslider.css">
@ -69,12 +72,12 @@
<div class="editbarright toolbar" id="editbar">
<ul>
<li onClick="window.padeditbar.toolbarClick('import_export');return false;">
<a id="exportlink" title="Export to different document formats">
<a id="exportlink" data-l10n-id="pad.importExport.export">
<div class="buttonicon buttonicon-import_export"></div>
</a>
</li>
</ul>
<a id="returnbutton">Return to pad</a>
<a id="returnbutton" data-l10n-id="timeslider.toolbar.returnbutton"></a>
</div>
<div>
@ -82,9 +85,9 @@
<span id="revision_label"></span>
<span id="revision_date"></span>
</h1>
<p>Authors:
<span id="authorsList">
<span>No Authors</span>
<p data-l10n-id="timeslider.toolbar.authors">
<span id="authorsList">
<span data-l10n-id="timeslider.toolbar.authorsList"></span>
</span> </p>
</div>
</div>
@ -101,70 +104,69 @@
</div><!-- /padpage -->
<div id="connectivity" class="popup">
<% e.begin_block("modals"); %>
<% e.begin_block("modals"); %>
<div class="connected visible">
<h2>Connected.</h2>
<h2 data-l10n-id="pad.modals.connected"></h2>
</div>
<div class="reconnecting">
<h1>Reestablishing connection...</h1>
<h1 data-l10n-id="pad.modals.reconnecting"></h1>
<p><img alt="" border="0" src="../../static/img/connectingbar.gif" /></p>
</div>
<div class="userdup">
<h1>Opened in another window.</h1>
<h2>You seem to have opened this pad in another browser window.</h2>
<p>If you'd like to use this window instead, you can reconnect.</p>
<button id="forcereconnect">Reconnect Now</button>
<h1 data-l10n-id="pad.modals.uderdup"></h1>
<h2 data-l10n-id="pad.modals.userdup.explanation"></h2>
<p data-l10n-id="pad.modals.connected.advice"></p>
<button id="forcereconnect" data-l10n-id="pad.modals.forcereconnect"></button>
</div>
<div class="unauth">
<h1>No Authorization.</h1>
<p>Your browser's credentials or permissions have changed while viewing this pad. Try reconnecting.</p>
<button id="forcereconnect">Reconnect Now</button>
<h1 data-l10n-id="pad.modals.unauth"></h1>
<p data-l10n-id="pad.modals.unauth.explanation"></p>
<button id="forcereconnect" data-l10n-id="pad.modals.forcereconnect"></button>
</div>
<div class="looping">
<h1>Disconnected.</h1>
<h2>We're having trouble talking to the EtherPad lite synchronization server.</h2>
<p>You may be connecting through an incompatible firewall or proxy server.</p>
<h1 data-l10n-id="pad.modals.looping"></h1>
<h2 data-l10n-id="pad.modals.looping.explanation"></h2>
<p data-l10n-id="pad.modals.looping.cause"></p>
</div>
<div class="initsocketfail">
<h1>Disconnected.</h1>
<h2>We were unable to connect to the EtherPad lite synchronization server.</h2>
<p>This may be due to an incompatibility with your web browser or internet connection.</p>
<h1 data-l10n-id="pad.modals.initsocketfail"></h1>
<h2 data-l10n-id="pad.modals.initsocketfail.explanation"></h2>
<p data-l10n-id="pad.modals.initsocketfail.cause"></p>
</div>
<div class="slowcommit">
<h1>Disconnected.</h1>
<h2>Server not responding.</h2>
<p>This may be due to network connectivity issues or high load on the server.</p>
<button id="forcereconnect">Reconnect Now</button>
<h1 data-l10n-id="pad.modals.slowcommit"></h1>
<h2 data-l10n-id="pad.modals.slowcommit.explanation"></h2>
<p data-l10n-id="pad.modals.slowcommit.cause"></p>
<button id="forcereconnect" data-l10n-id="pad.modals.forcereconnect"></button>
</div>
<div class="deleted">
<h1>Disconnected.</h1>
<p>This pad was deleted.</p>
<h1 data-l10n-id="pad.modals.deleted"></h1>
<p data-l10n-id="pad.modals.deleted.explanation"></p>
</div>
<div class="disconnected">
<h1>Disconnected.</h1>
<h2>Lost connection with the EtherPad lite synchronization server.</h2>
<p>This may be due to a loss of network connectivity. If this continues to happen, please let us know</p>
<button id="forcereconnect">Reconnect Now</button>
<h1 data-l10n-id="pad.modals.disconnected"></h1>
<h2 data-l10n-id="pad.modals.disconnected.explanation"></h2>
<p data-l10n-id="pad.modals.disconnected.cause"></p>
<button id="forcereconnect" data-l10n-id="pad.modals.forcereconnect"></button>
</div>
<form id="reconnectform" method="post" action="/ep/pad/reconnect" accept-charset="UTF-8" style="display: none;">
<input type="hidden" class="padId" name="padId">
<input type="hidden" class="diagnosticInfo" name="diagnosticInfo">
<input type="hidden" class="missedChanges" name="missedChanges">
</form>
<% e.end_block(); %>
<% e.end_block(); %>
</div>
<!-- export code -->
<div id="importexport">
<div id="export" class="popup">
Export current version as:
<a id="exporthtmla" target="_blank" class="exportlink"><div class="exporttype" id="exporthtml">HTML</div></a>
<a id="exportplaina" target="_blank" class="exportlink"><div class="exporttype" id="exportplain">Plain text</div></a>
<a id="exportworda" target="_blank" class="exportlink"><div class="exporttype" id="exportword">Microsoft Word</div></a>
<a id="exportpdfa" target="_blank" class="exportlink"><div class="exporttype" id="exportpdf">PDF</div></a>
<a id="exportopena" target="_blank" class="exportlink"><div class="exporttype" id="exportopen">OpenDocument</div></a>
<a id="exportdokuwikia" target="_blank" class="exportlink"><div class="exporttype" id="exportdokuwiki">DokuWiki text</div></a>
<div id="export" class="popup" data-l10n-id="timeslider.exportCurrent">
<a id="exporthtmla" target="_blank" class="exportlink"><div class="exporttype" id="exporthtml" data-l10n-id="pad.importExport.exporthtml"></div></a>
<a id="exportplaina" target="_blank" class="exportlink"><div class="exporttype" id="exportplain" data-l10n-id="pad.importExport.exportplain"></div></a>
<a id="exportworda" target="_blank" class="exportlink"><div class="exporttype" id="exportword" data-l10n-id="pad.importExport.exportword"></div></a>
<a id="exportpdfa" target="_blank" class="exportlink"><div class="exporttype" id="exportpdf" data-l10n-id="pad.importExport.exportpdf"></div></a>
<a id="exportopena" target="_blank" class="exportlink"><div class="exporttype" id="exportopen" data-l10n-id="pad.importExport.exportopen"></div></a>
<a id="exportdokuwikia" target="_blank" class="exportlink"><div class="exporttype" id="exportdokuwiki" data-l10n-id="pad.importExport.exportdokuwiki"></div></a>
</div>
</div>
@ -182,6 +184,9 @@
var clientVars = {};
(function () {
// Set language for l10n
var language = document.cookie.match(/language=(\w{2})/);
document.documentElement.lang = language? language[1] : 'en';
var pathComponents = location.pathname.split('/');

View file

@ -0,0 +1,83 @@
describe("Language select and change", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(60000);
});
it("makes text german", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//click on the settings button to make settings visible
var $settingsButton = chrome$(".buttonicon-settings");
$settingsButton.click();
//click the language button
var $language = chrome$("#languagemenu");
var $languageoption = $language.find("[value=de]");
//select german
$languageoption.attr('selected','selected');
$language.change();
helper.waitFor(function(){
var $boldButton = chrome$(".buttonicon-bold").parent();
//get the title of the bold button
var boldButtonTitle = $boldButton[0]["title"];
return boldButtonTitle !== undefined;
}).done(function(){
//get the value of the bold button
var $boldButton = chrome$(".buttonicon-bold").parent();
//get the title of the bold button
var boldButtonTitle = $boldButton[0]["title"];
//check if the language is now german
expect(boldButtonTitle).to.be("Fett (Strg-B)");
done();
});
});
it("makes text English", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//click on the settings button to make settings visible
var $settingsButton = chrome$(".buttonicon-settings");
$settingsButton.click();
//click the language button
var $language = chrome$("#languagemenu");
var $languageoption = $language.find("[value=en]");
//select german
$languageoption.attr('selected','selected');
$language.change();
helper.waitFor(function(){
var $boldButton = chrome$(".buttonicon-bold").parent();
//get the title of the bold button
var boldButtonTitle = $boldButton[0]["title"];
return boldButtonTitle !== undefined;
}).done(function(){
//get the value of the bold button
var $boldButton = chrome$(".buttonicon-bold").parent();
//get the title of the bold button
var boldButtonTitle = $boldButton[0]["title"];
//check if the language is now English
expect(boldButtonTitle).to.be("Bold (Ctrl-B)");
done();
});
});
});