diff --git a/customize.dist/application_config.js b/customize.dist/application_config.js
index d7e9141c1..e4561df24 100644
--- a/customize.dist/application_config.js
+++ b/customize.dist/application_config.js
@@ -4,7 +4,7 @@ define(function() {
/* Select the buttons displayed on the main page to create new collaborative sessions
* Existing types : pad, code, poll, slide
*/
- config.availablePadTypes = ['pad', 'code', 'poll', 'slide'];
+ config.availablePadTypes = ['pad', 'code', 'slide', 'poll'];
return config;
});
diff --git a/customize.dist/index.html b/customize.dist/index.html
index f66d4dbcf..6c03cb30b 100644
--- a/customize.dist/index.html
+++ b/customize.dist/index.html
@@ -40,7 +40,7 @@
- Unity is Strength - Collaboration is Key
+ Unity is Strength - Collaboration is Key
diff --git a/customize.dist/main.js b/customize.dist/main.js
index 2622eef1b..732bce4b8 100644
--- a/customize.dist/main.js
+++ b/customize.dist/main.js
@@ -28,8 +28,9 @@ define([
var forgetPad = Cryptpad.forgetPad;
var displayCreateButtons = function () {
+ var $parent = $('#buttons');
Config.availablePadTypes.forEach(function (el) {
- $('#create-' + el).show();
+ $('#create-' + el).detach().appendTo($parent).show();
});
};
diff --git a/customize.dist/messages.js b/customize.dist/messages.js
index bb314e002..8cb4e1918 100644
--- a/customize.dist/messages.js
+++ b/customize.dist/messages.js
@@ -1,11 +1,20 @@
define(['/customize/languageSelector.js',
'/customize/translations/messages.js',
+ '/customize/translations/messages.es.js',
'/customize/translations/messages.fr.js',
- '/bower_components/jquery/dist/jquery.min.js'], function(LS, Default, French) {
+
+ // 1) additional translation files can be added here...
+
+ '/bower_components/jquery/dist/jquery.min.js'],
+
+ // 2) name your language module here...
+ function(LS, Default, Spanish, French) {
var $ = window.jQuery;
+ // 3) add your module to this map so it gets used
var map = {
- 'fr': French
+ 'fr': French,
+ 'es': Spanish,
};
var defaultLanguage = 'en';
@@ -32,6 +41,24 @@ define(['/customize/languageSelector.js',
}
messages._initSelector = LS.main;
+ messages._checkTranslationState = function () {
+ var missing = [];
+ Object.keys(map).forEach(function (code) {
+ var translation = map[code];
+ Object.keys(Default).forEach(function (k) {
+ if (/^_/.test(k) || /nitialState$/.test(k)) { return; }
+ if (!translation[k]) {
+ var warning = "key [" + k + "] is missing from translation [" + code + "]";
+ missing.push(warning);
+ }
+ });
+ if (typeof(translation._languageName) !== 'string') {
+ var warning = 'key [_languageName] is missing from translation [' + code + ']';
+ missing.push(warning);
+ }
+ });
+ return missing;
+ };
// Get keys with parameters
messages._getKey = function (key, argArray) {
diff --git a/customize.dist/translations/README.md b/customize.dist/translations/README.md
new file mode 100644
index 000000000..8bde27e08
--- /dev/null
+++ b/customize.dist/translations/README.md
@@ -0,0 +1,129 @@
+# Adding Translations
+
+To illustrate the process of translating, this guide will make an english-pirate translation of Cryptpad.
+We'll assume that you have a work locally-installed, properly functioning installation of Cryptpad.
+If you don't have Cryptpad installed locally, start by following the steps in the main readme.
+
+## Getting started
+
+Once everything is working, copy the default (English) source file (`/customize.dist/translations/messages.js`) to a file named according to your language's [ISO 639-1 Code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes), like `/customize.dist/translations/messages.fr.js`.
+There is no ISO 639-1 language code for _English-pirate_, so we'll just call it `messages.pirate.js`.
+
+```Bash
+cd /customize.dist/translations/
+cp messages.js messages.pirate.js
+```
+
+## Including your translation
+
+To include your translation in the list, you'll need to add it to `/customize.dist/messages.js`.
+There are comments indicating what to modify in three places:
+
+```javascript
+define(['/customize/languageSelector.js',
+ '/customize/translations/messages.js',
+ '/customize/translations/messages.es.js',
+ '/customize/translations/messages.fr.js',
+
+ // 1) additional translation files can be added here...
+
+ '/bower_components/jquery/dist/jquery.min.js'],
+
+ // 2) name your language module here...
+ function(LS, Default, Spanish, French) {
+ var $ = window.jQuery;
+
+ // 3) add your module to this map so it gets used
+ var map = {
+ 'fr': French,
+ 'es': Spanish,
+ };
+```
+
+We need to modify these three places to include our file:
+
+```javascript
+define(['/customize/languageSelector.js',
+ '/customize/translations/messages.js',
+ '/customize/translations/messages.es.js',
+ '/customize/translations/messages.fr.js',
+
+ // 1) additional translation files can be added here...
+ '/customize/translations/messages.pirate.js', // add our module via its path
+
+ '/bower_components/jquery/dist/jquery.min.js'],
+
+ // 2) name your language module here...
+ function(LS, Default, Spanish, French, Pirate) { // name our module 'Pirate' for use as a variable
+ var $ = window.jQuery;
+
+ // 3) add your module to this map so it gets used
+ var map = {
+ 'fr': French,
+ 'es': Spanish,
+ 'pirate': Pirate, // add our module to the map of languages
+ };
+```
+
+Note that the path to the file is `/customize/translations/`, not `/customize.dist/translations`.
+Cryptpad's server is configured to search for files in `/customize/` first.
+If a file is not found, it falls back to `/customize.dist/`.
+This allows administrators of a Cryptpad installation to override the default behaviour with their own files.
+
+We want translations to be the default behaviour, so we'll place it in `/customize.dist/translations/`, but resolve it via `/customize/translations/`.
+
+The second and third steps are simpler.
+Just add your module in a similar fashion to the existing translations, save your changes, and close `/customize.dist/messages.js`.
+That's all!
+
+
+## Actually translating content
+
+Now we can go back to our file, `/customize.dist/translations/messages.pirate.js` and start to add our Pirate-language customizations.
+
+Open the translation file you created in `/customize.dist/translations/`.
+You should see something like:
+
+```javascript
+define(function () {
+ var out = {};
+
+ // translations must set this key for their language to be available in
+ // the language dropdowns that are shown throughout Cryptpad's interface
+ out._languageName = 'English';
+```
+
+Now you just need to work through this file, updating the strings like so:
+
+```javascript
+define(function () {
+ var out = {};
+
+ // translations must set this key for their language to be available in
+ // the language dropdowns that are shown throughout Cryptpad's interface
+ out._languageName = 'Pirate';
+```
+
+It's important that you modify just the string, and not the variable name which is used to access its content.
+For instance, changing `_languageName` to `_language_name` would make the string `'Pirate'` inaccessible to the rest of the codebase.
+
+If a key is not found in your translation, the default English version of that key will be used.
+This is to make sure that buttons and other interface elements are not empty, but it's obviously not ideal.
+
+## Verifying Your Translations
+
+It's advisable to save your translation file frequently, and reload Cryptpad in your browser to check that there are no errors in your translation file.
+If there are any errors in your code, the file will fail to parse, and the page will no load correctly.
+
+Checking frequently will make it easier to know which change caused the error.
+
+Additionally, we advise using the apps and visiting the various pages, to make sure that your translations make sense in context.
+
+When you're happy with your translation file, you can visit http://localhost:3000/assert/ to view Cryptpad's tests.
+Among other things, these tests will check to make sure that your translation has an entry for every entry in the default English translation.
+
+## Getting Help
+
+If you have any issues, reach out via any of the methods listed in the readme under **Contacting Us**.
+We're happy to help.
+
diff --git a/customize.dist/translations/messages.es.js b/customize.dist/translations/messages.es.js
new file mode 100644
index 000000000..a7b9740b9
--- /dev/null
+++ b/customize.dist/translations/messages.es.js
@@ -0,0 +1,244 @@
+define(function () {
+ var out = {};
+
+ out._languageName = 'Español';
+
+ out.main_title = "Cryptpad: Zero Knowledge, Editor Colaborativo en Tiempo Real";
+ out.main_slogan = "La unidad es la fuerza - la colaboración es la clave";
+
+ out.type = {};
+ out.type.pad = 'Pad';
+ out.type.code = 'Código';
+ out.type.poll = 'Encuesta';
+ out.type.slide = 'Presentación';
+
+ out.errorBox_errorType_disconnected = 'Conexión perdida';
+ out.errorBox_errorExplanation_disconnected = [
+ 'Se perdio la conexion con el servidor, tu puedes reconectarte actualizando la pagina o revisando tu trabajo ',
+ 'haciendo click por fuera de este cuadro.'
+ ].join('');
+
+ out.common_connectionLost = 'Conexión perdida con el servidor';
+
+ out.disconnected = "Desconectado";
+ out.synchronizing = "Sincronización";
+ out.reconnecting = "Reconectando";
+ out.lag = "Retraso";
+ out.readOnly = 'Solo lectura';
+ out.anonymous = 'Anónimo';
+ out.yourself = "tú mismo";
+ out.anonymousUsers = "usuarios anónimos";
+ out.anonymousUser = "usuario anónimo";
+ out.shareView = "URL de sólo lectura";
+ out.shareEdit = "Editar URL";
+ out.users = "Usuarios";
+ out.and = "y";
+ out.viewer = "espectador";
+ out.viewers = "espectadores";
+ out.editor = "editor";
+ out.editors = "editores";
+
+ out.editingAlone = 'Editar solo';
+ out.editingWithOneOtherPerson = 'Editar con otra persona';
+ out.editingWith = 'Editar con ';
+ out.otherPeople = 'Otras personas';
+ out.disconnected = 'Desconectado';
+ out.synchronizing = 'Sincronizando';
+ out.reconnecting = 'Reconectando...';
+ out.lag = 'Retraso';
+ out.readonly = 'Solo lectura';
+ out.nobodyIsEditing = 'Nadie está editando';
+ out.onePersonIsEditing = 'Una personas está editando';
+ out.peopleAreEditing = '{0} personas están editando';
+ out.oneViewer = '1 visualizando';
+ out.viewers = '{0} están visualizando';
+ out.anonymous = "Actualmente eres anónimo";
+
+ out.greenLight = "Todo esta trabajando bién";
+ out.orangeLight = "Tu conexion es lenta y podria impactar en el desempeño de tu experiencia";
+ out.redLight = "¡Has sido desconectado de la sesión! ";
+
+ out.importButton = 'IMPORTAR';
+ out.importButtonTitle = 'Importar un documento de tus archivos locales';
+
+ out.exportButton = 'EXPORTAR';
+ out.exportButtonTitle = 'Exportar este documento a un archivo local';
+ out.exportPrompt = '¿Como te gustaria nombra tu archivo ?';
+
+ out.back = '⇐ Atras';
+ out.backToCryptpad = '⇐ Atras a Cryptpad';
+
+ out.userButton = 'USUARIO';
+ out.userButtonTitle = 'Cambiar tu nombre de usuario';
+ out.changeNamePrompt = 'Cambiar tu nombre: ';
+
+ out.renameButton = 'RENOMBRAR';
+ out.renameButtonTitle = 'Cambiar el titulo del documento listado en tu pagina de inicio';
+ out.renamePrompt = 'Como titularias este noja?';
+ out.renameConflict = 'Otra nota tiene ya ese titulo';
+ out.clickToEdit = "Haz click para editar";
+
+ out.forgetButton = 'OLVIDAR';
+ out.forgetButtonTitle = 'Eliminar este documento de la lista en la pagina de inicio';
+ out.forgetPrompt = 'Presiona OK, removera la URL para esta nota desde el almacenamiento local, ¿Esta seguro?';
+
+ out.shareButton = 'COMPARTIR';
+ out.shareButtonTitle = "Copiar URL al portapapeles";
+ out.shareSuccess = '¡URL Copiada al portapapeles!';
+ out.shareFailed = "Fallo al copiar URL al portapapeles";
+
+ out.presentButton = 'PRESENTAR';
+ out.presentButtonTitle = "Ingresar en modo presentación";
+ out.presentSuccess = 'ESC para salir del modo presentación';
+ out.sourceButton = 'VER CÓDIGO FUENTE';
+ out.sourceButtonTitle = "Abandonar modo presentación";
+
+ out.backgroundButton = 'COLOR DE FONDO';
+ out.backgroundButtonTitle = 'Cambiar el color de fondo en el modo presentación';
+ out.colorButton = 'COLOR DE TEXTO';
+ out.colorButtonTitle = 'Cambiar el color de texto en el modo presentación';
+
+ out.commitButton = 'COMMIT';
+
+ out.getViewButton = 'URL SOLO-LECTURA';
+ out.getViewButtonTitle = 'Obtener URL de solo lectura para este documento';
+ out.readonlyUrl = 'Documento de solo lectura';
+ out.copyReadOnly = "Copiar URL al portapapeles";
+ out.openReadOnly = "Abrir en nueva pestaña";
+
+ // TODO VERIFY
+ out.editShare = "URL de edición compartida";
+ out.editShareTitle = "Copiar la URL de edición al portapapeles";
+ out.viewShare = "Compartir vista URL";
+ out.viewShareTitle = "Copiar la URL de solo lectura al portapapeles";
+ out.viewOpen = "Ver en nueva pestaña";
+ out.viewOpenTitle = "Abrir el documento en modo de sólo lectura en una nueva pestaña";
+
+ out.notifyJoined = "{0} se ha unido a la sesión de colaboración";
+ out.notifyRenamed = "{0} ahora se conoce como {1}";
+ out.notifyLeft = "{0} ha dejado la sesión de colaboración";
+
+ out.disconnectAlert = '¡Conexión de Red perdida!';
+
+ out.tryIt = '¡PROBARLO!';
+ out.recentPads = 'Tus notas recientes (están almacenadas en el navegador)';
+
+ out.okButton = 'OK (enter)';
+ out.cancelButton = 'Cancelar (esc)';
+
+ out.loginText = '
Tu nombre de usuario y password son usados para generar una llave unica que es desconocida por nuestro servidor.
\n' +
+ '
Se cuidados no olvides tus credenciales, son imposibles de recuperar
';
+
+ out.forget = "Olvidar";
+
+ // Polls
+
+ out.poll_title = "Zero Knowledge selector de fecha";
+ out.poll_subtitle = "Zero Knowledge, agenda en
tiempo real ";
+
+ out.poll_p_save = "Tus configuraciones son actualizadas instantaneamente, no es necesario guardar cambios.";
+ out.poll_p_encryption = "Todos los datos de entrada son cifrados, solo las personas que posee el link tiene acceso. Incluso desde el servidor no puede ver tus cambios.";
+ out.poll_p_howtouse = "Ingresa tu nombre en el campo de entrada en la parte inferior y verificalo";
+
+ out.promptName = "¿ Cual es tu nombre ?";
+
+ out.wizardButton = 'ASISTENTE';
+ out.wizardLog = "Presiona el boton en la parte superior izquierda para regresar a la encuesta";
+ out.wizardTitle = "Utiliza el asistente para crear tu encuesta";
+ out.wizardConfirm = "¿Estas realmente seguro de agregar estas opciones a tu encuesta?";
+
+ out.poll_closeWizardButton = "Cerrar el asistente";
+ out.poll_closeWizardButtonTitle = "Cerrar el asistente";
+ out.poll_wizardComputeButton = "Computar opciones";
+ out.poll_wizardClearButton = "Limpiar tabla";
+ out.poll_wizardDescription = "Automaticamente crear un number de opciones ingresando cualquier numero de fechas y segmentos de tiempo";
+ out.poll_wizardAddDateButton = "+ Fechas";
+ out.poll_wizardAddTimeButton = "+ Horas";
+
+ out.poll_addUserButton = "+ Usuarios";
+ out.poll_addUserButtonTitle = "Click para adicionar usuario";
+ out.poll_addOptionButton = "+ Opciones";
+ out.poll_addOptionButtonTitle = "Click para adicionar una opción";
+ out.poll_addOption = "Proponer una opción";
+ out.poll_optionPlaceholder = "Opción";
+ out.poll_addUser = "Ingresar un nombre";
+ out.poll_userPlaceholder = "Tu nombre";
+ out.poll_removeOption = "¿Seguro que te gustaria eliminar esta opción?";
+ out.poll_removeOptionTitle = "Eliminar la fila";
+ out.poll_removeUser = "¿Seguro que te gustaria eliminar este usuario?";
+ out.poll_removeUserTitle = "Eliminar la columna";
+ out.poll_editOption = "¿Seguro que te gustaria editar esta opción?";
+ out.poll_editOptionTitle = "Editar la fila";
+ out.poll_editUser = "¿Seguro que te gustaria editar este usuario?";
+ out.poll_editUserTitle = "Editar la columna";
+
+ out.poll_titleHint = "Titulo";
+ out.poll_descriptionHint = "Descripción";
+
+ // index.html
+
+ out.main_p1 = 'CryptPad es un editor en tiempo
zero knowledge. El cifrado es llevado acabo en tu navegador protegiendo los datos de, sistemas en la nube, and la NSA. La clave cifrado privada es almacenada en la URL
fragment identifier lo cual no es nunca enviado al servidor pero esta disponible para el javascript compartiendo la URL, tu autorizas a otros quienes desean participar.';
+ out.main_p2 = 'Este proyecto usa
CKEditor un editor de texto,
CodeMirror, y
ChainPad un motor de tiempo real.';
+ out.main_howitworks = '¿Como Funciona?';
+
+ out.main_howitworks_p1 = "CryptPad utiliza una variante del algoritmo de
transformación Operacional que es capaz de encontrar el consenso distribuido usando un
Nakamoto Blockchain, una construcción popularizada por
Bitcoin . De esta manera el algoritmo puede evitar la necesidad de un servidor central para resolver Conflictos de Edición de Transformación Operacional y sin necesidad de resolver conflictos, el servidor puede mantenerse inconsciente del contenido que se está editando en el pad.";
+
+
+ out.main_about = 'Acerca de';
+ out.main_about_p1 = 'Tu puedes leer mas acerca de nuestra
politica de privacidad y
terminos de servicio.';
+
+ out.main_about_p2 = 'Si tu tienes preguntas o comentarios, tu puedes
tweetearnos ,o abrir un issue
en github, ven y di hola a nuestro canal de irc (
irc.freenode.net), o
envianos un email.';
+
+ out.table_type = 'Tipo';
+ out.table_link = 'Link';
+ out.table_created = 'Creado';
+ out.table_last = 'Ultimo Acceso';
+
+ out.button_newpad = 'CREAR NUEVA NOTA DE TEXTO ENRIQUECIDO';
+ out.button_newcode = 'CREAR NUEVA NOTA DE CÓDIGO';
+ out.button_newpoll = 'CREAR NUEVA ENCUESTA';
+ out.button_newslide = 'CREAR NUEVA PRESENTACIÓN';
+
+ // privacy.html
+
+ out.policy_title = 'Cryptpad Política de privacidad';
+ out.policy_whatweknow = 'Que sabemos sobre tí';
+ out.policy_whatweknow_p1 = 'Como una aplicación que esta hospedada en la red, Cryptpad tiene acceso a los metadatos expuestos por el protocolo HTTP. Esto incluye tu direccion IP, y otros headers HTTP que puede ser usados para identificar particularmente tu navegador. Tu puedes comprar la informacion que comparte tu navegador visitando
WhatIsMyBrowser.com.';
+ out.policy_whatweknow_p2 = 'Nosotros usamos
Piwik, an open source analytics platform, to learn more about our users. Piwik tells us about how you found Cryptpad, via direct entry, through a search engine, or via a referral from another web service like Reddit or Twitter. We also learn when you visit, what links you click while on our informational pages, and how long you stay on a particular page.';
+ out.policy_whatweknow_p3 = 'Estas herramientas de estadística son usadas solamente para informacion local . Nosotros no recolectamos cualquier informacion sobre el uso de nuestro zero-knowledge application.';
+ out.policy_howweuse = 'Como nosotros usamos lo que aprendemos';
+ out.policy_howweuse_p1 = 'Nosotros usamos esta informacion para tomar mejores decisiones como promover y promocionar Cryptpad, para evaluar cual de nuestros esfuerzos pasados han sido exitosos. La informacion sobre tu ubicacion nos permite conocer si nosotros debemos considerar mejor soporte para otros idiomas diferentes al ingles.';
+ out.policy_howweuse_p2 = "La informacion sobre tu navegador (si es un sistema operativo de escritorio o movil) nos ayuda a tomar medidads en las caracteristicas que debemos priorizar y mejorar. Nuestro equipo de desarrollo es pequeño, intentamos hacer elecciones que beneficien la mayoria de los usuarios' experiencia como sea posible.";
+ out.policy_whatwetell = 'Lo que nosotros le decimos a otros sobre tí';
+ out.policy_whatwetell_p1 = 'No suministramos información a terceros, ni la que usted nos proporciona a nosotros a menos de ser obligados legalmente a hacerlo.';
+ out.policy_links = 'Links a Otros sitios';
+ out.policy_links_p1 = 'Este sitio contiene links a otros sitios, incluyendo algunos producidos por otras organizaciones. Nosotros no nos responsabilisamos por el tratamiento, privacidad de los datos y contenido de sitios externos. Como regla general, los links externos son abiertos una nueva pestaña del navegador, para hacerlo claro tu estas abandonando Cryptpad.fr.';
+ out.policy_ads = 'Anuncio';
+ out.policy_ads_p1 = 'Nosotros no mostramos anuncios online, pensamos que podemos linkear a las personas que financian nuestra investigación.';
+ out.policy_choices = 'Eleciones que tu tienes';
+ out.policy_choices_open = 'Nuestro código es open source, Entonces tu siempre tienes la opcion crear tu propia estancia de Cryptpad.';
+ out.policy_choices_vpn = 'Si tu deseas usar nuestra estancia, pero no deseas exponer tu dirección IP, tu puedes proteger tu dirección IP utilizando
Tor browser bundle, o una
VPN.';
+ out.policy_choices_ads = 'If usted desea blockear rastreadores, y plataformas similares puedes utilizar herramientas como
Privacy Badger.';
+
+ // terms.html
+
+ out.tos_title = "Cryptpad Términos de Servicio";
+ out.tos_legal = "Porfavor no seas malicioso, abusivo o realices prácticas ilegales.";
+ out.tos_availability = "Nosotros esperamos que encuentres este servicio util, pero nuestra disponibilidad o desempeño no pueden ser garantizados. Por favor exporta tus datos regularmente.";
+ out.tos_e2ee = "Cryptpad Los documentos pueden ser leidos o modificados para alguien puede ser invitado o por el contrario obtener un fragmento del documento. Nosotros recomendamos que tu uses cifrado punto a punto(e2ee) para compartir URLs, no asumimos ninguna responsabilidad en el evento de que existan fugas de URLs.";
+ out.tos_logs = "Los metadatos entregados por el navegador a el servidor pueden ser logueados y almacenados para propuestas de mantener el servicio.";
+ out.tos_3rdparties = "Nosotros no proveemos datos individualizados a terceros a menos que sea requerido por la ley.";
+
+ // BottomBar.html
+
+ out.bottom_france = '
Made with en ';
+ out.bottom_support = '
An Labs Project with the support of
';
+
+ // Header.html
+
+ out.header_france = '
With from by ';
+ out.header_support = '
';
+ out.header_logoTitle = 'Ir a la pagina principal';
+
+ return out;
+});
diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js
index 9c64e1f4f..49fd33731 100644
--- a/customize.dist/translations/messages.fr.js
+++ b/customize.dist/translations/messages.fr.js
@@ -4,6 +4,7 @@ define(function () {
out._languageName = "Français";
out.main_title = "Cryptpad: Editeur collaboratif en temps réel, zero knowledge";
+ out.main_slogan = "L'unité est la force, la collaboration est la clé";
out.type = {};
out.type.pad = 'Pad';
@@ -206,7 +207,7 @@ define(function () {
out.main_p1 = 'CryptPad est l\'éditeur collaboratif en temps réel
zero knowledge. Le chiffrement est effectué depuis votre navigateur, ce qui protège les données contre le serveur, le cloud, et la NSA. La clé de chiffrement est stockée dans l\'
identifieur de fragment de l\'URL qui n\'est jamais envoyée au serveur mais est accessible depuis javascript, de sorte qu\'en partageant l\'URL, vous donnez l\'accès au pad à ceux qui souhaitent participer.';
out.main_p2 = 'Ce projet utilise l\'éditeur visuel (WYSIWYG)
CKEditor, l\'éditeur de code source
CodeMirror, et le moteur temps-réel
ChainPad.';
out.main_howitworks = 'Comment ça fonctionne';
- out.main_howitworks_p1 = 'CryptPad utilise une variante de l\'algorithme d\'
Operational transformation qui est capable de trouver un consensus distribué en utilisant une chaîne de bloc Nakamoto, un outil popularisé par le
Bitcoin. De cette manière, l\'algorithme évite la nécessité d\'utiliser un serveur central pour résoudre les conflits d\'édition de l\'Operational Transformation, et sans ce besoin de résolution des conflits le serveur peut rester ignorant du contenu qui est édité dans le pad.';
+ out.main_howitworks_p1 = 'CryptPad utilise une variante de l\'algorithme d\'
Operational transformation qui est capable de trouver un consensus distribué en utilisant
une chaîne de bloc Nakamoto, un outil popularisé par le
Bitcoin. De cette manière, l\'algorithme évite la nécessité d\'utiliser un serveur central pour résoudre les conflits d\'édition de l\'Operational Transformation, et sans ce besoin de résolution des conflits le serveur peut rester ignorant du contenu qui est édité dans le pad.';
out.main_about = 'À propos';
out.main_about_p1 = 'Vous pouvez en apprendre davantage sur notre
politique de confidentialité et nos
conditions d\'utilisation.';
out.main_about_p2 = 'Si vous avez des questions ou commentaires, vous pouvez
nous tweeter, ouvrir une issue sur
Github, venir dire bonjour sur IRC (
irc.freenode.net), ou
nous envoyer un email.';
@@ -259,7 +260,6 @@ define(function () {
// Header.html
out.header_france = '
Fait avec en par ';
- out.header_xwiki = '
';
out.header_support = '
';
out.header_logoTitle = "Aller vers la page d'accueil";
diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js
index f5f80e4a1..846e32a16 100644
--- a/customize.dist/translations/messages.js
+++ b/customize.dist/translations/messages.js
@@ -1,9 +1,12 @@
define(function () {
var out = {};
+ // translations must set this key for their language to be available in
+ // the language dropdowns that are shown throughout Cryptpad's interface
out._languageName = 'English';
out.main_title = "Cryptpad: Zero Knowledge, Collaborative Real Time Editing";
+ out.main_slogan = "Unity is Strength - Collaboration is Key";
out.type = {};
out.type.pad = 'Pad';
@@ -206,7 +209,7 @@ define(function () {
out.main_p1 = 'CryptPad is the
zero knowledge realtime collaborative editor. Encryption carried out in your web browser protects the data from the server, the cloud, and the NSA. The secret encryption key is stored in the URL
fragment identifier which is never sent to the server but is available to javascript so by sharing the URL, you give authorization to others who want to participate.';
out.main_p2 = 'This project uses the
CKEditor Visual Editor,
CodeMirror, and the
ChainPad realtime engine.';
out.main_howitworks = 'How It Works';
- out.main_howitworks_p1 = 'CryptPad uses a variant of the
Operational transformation algorithm which is able to find distributed consensus using a Nakamoto Blockchain, a construct popularized by
Bitcoin. This way the algorithm can avoid the need for a central server to resolve Operational Transform Edit Conflicts and without the need for resolving conflicts, the server can be kept unaware of the content which is being edited on the pad.';
+ out.main_howitworks_p1 = 'CryptPad uses a variant of the
Operational transformation algorithm which is able to find distributed consensus using a
Nakamoto Blockchain, a construct popularized by
Bitcoin. This way the algorithm can avoid the need for a central server to resolve Operational Transform Edit Conflicts and without the need for resolving conflicts, the server can be kept unaware of the content which is being edited on the pad.';
out.main_about = 'About';
out.main_about_p1 = 'You can read more about our
privacy policy and
terms of service.';
@@ -260,7 +263,10 @@ define(function () {
// Header.html
out.header_france = '
With from by ';
- out.header_xwiki = '
';
+
+
+ // TODO Hardcode cause YOLO
+ //out.header_xwiki = '
';
out.header_support = '
';
out.header_logoTitle = 'Go to the main page';
diff --git a/readme.md b/readme.md
index c80766ad0..e2dab9d9c 100644
--- a/readme.md
+++ b/readme.md
@@ -101,6 +101,21 @@ the battery out of your computer before it spawns Agent Smith.
Still there are other low-lives in the world so using CryptPad over HTTPS is probably a good idea.
+## Translations
+
+We'd like to make it easy for more people to use encryption in their routine activities.
+As such, we've tried to make language-specific parts of Cryptpad translatable. If you're
+able to translate Cryptpad's interface, and would like to help, please contact us!
+
+You can also see [our translation guide](/customize.dist/translations/README.md).
+
+## Contacting Us
+
+You can reach members of the Cryptpad development team on [twitter](https://twitter.com/cryptpad),
+via our [github issue tracker](https://github.com/xwiki-labs/cryptpad/issues/), on the
+[freenode](http://webchat.freenode.net/?channels=%23cryptpad&uio=MT1mYWxzZSY5PXRydWUmMTE9Mjg3JjE1PXRydWUe7)
+irc network, or by [email](mailto:research@xwiki.com).
+
## Contributing
We love Open Source and we love contribution. It is our intent to keep this project available
diff --git a/www/assert/main.js b/www/assert/main.js
index dc3c2087e..f3b6c6fe2 100644
--- a/www/assert/main.js
+++ b/www/assert/main.js
@@ -4,7 +4,8 @@ define([
'/bower_components/hyperjson/hyperjson.js',
'/bower_components/textpatcher/TextPatcher.amd.js',
'json.sortify',
-], function (jQuery, Hyperjson, TextPatcher, Sortify) {
+ '/common/cryptpad-common.js',
+], function (jQuery, Hyperjson, TextPatcher, Sortify, Cryptpad) {
var $ = window.jQuery;
window.Hyperjson = Hyperjson;
window.TextPatcher = TextPatcher;
@@ -140,6 +141,19 @@ define([
strungJSON(orig);
});
+ assert(function () {
+ var missing = Cryptpad.Messages._checkTranslationState();
+
+ if (missing.length !== 0) {
+ missing.forEach(function (msg) {
+ console.log('* ' + msg);
+ });
+
+ return false;
+ }
+ return true;
+ }, "expected all translation keys in default language to be present in all translations. See console for details.");
+
var swap = function (str, dict) {
return str.replace(/\{\{(.*?)\}\}/g, function (all, key) {
return typeof dict[key] !== 'undefined'? dict[key] : all;
diff --git a/www/code/main.js b/www/code/main.js
index ee4fd2f0f..c5e605ef4 100644
--- a/www/code/main.js
+++ b/www/code/main.js
@@ -253,18 +253,21 @@ define([
var clike = /^\s*(\/\*|\/\/)(.*)?(\*\/)*$/;
if (clike.test(line)) {
line.replace(clike, function (a, one, two) {
+ if (!(two && two.replace)) { return; }
text = two.replace(/\*\/\s*$/, '').trim();
});
return true;
}
+
+ // TODO make one more pass for multiline comments
});
return text.trim();
};
- var suggestName = function () {
+ var suggestName = function (fallback) {
if (document.title === defaultName) {
- return getHeadingText() || "";
+ return getHeadingText() || fallback || "";
} else {
return document.title || getHeadingText() || defaultName;
}
@@ -275,7 +278,7 @@ define([
var ext = Modes.extensionOf(module.highlightMode);
- var title = Cryptpad.fixFileName(suggestName()) + ext;
+ var title = Cryptpad.fixFileName(suggestName('cryptpad')) + (ext || '.txt');
Cryptpad.prompt(Messages.exportPrompt, title, function (filename) {
if (filename === null) { return; }
@@ -495,9 +498,7 @@ define([
}
// set the hash
- if (!readOnly) {
- window.location.hash = editHash;
- }
+ if (!readOnly) { Cryptpad.replaceHash(editHash); }
Cryptpad.getPadTitle(function (err, title) {
if (err) {
diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js
index 120be68dc..64b0a167b 100644
--- a/www/common/cryptpad-common.js
+++ b/www/common/cryptpad-common.js
@@ -28,6 +28,7 @@ define([
var common = {
User: User,
+ Messages: Messages,
};
var store;
var fsStore;
@@ -238,6 +239,14 @@ define([
return secret;
};
+ var replaceHash = common.replaceHash = function (hash) {
+ if (window.history && window.history.replaceState) {
+ if (!/^#/.test(hash)) { hash = '#' + hash; }
+ return void window.history.replaceState({}, window.document.title, hash);
+ }
+ window.location.hash = hash;
+ };
+
var storageKey = common.storageKey = 'CryptPad_RECENTPADS';
/*
diff --git a/www/pad/main.js b/www/pad/main.js
index 310a3d353..457913cb5 100644
--- a/www/pad/main.js
+++ b/www/pad/main.js
@@ -35,8 +35,23 @@ define([
window.Toolbar = Toolbar;
window.Hyperjson = Hyperjson;
+ var slice = function (coll) {
+ return Array.prototype.slice.call(coll);
+ };
+
+ var removeListeners = function (root) {
+ slice(root.attributes).map(function (attr) {
+ if (/^on/.test(attr.name)) {
+ root.attributes.removeNamedItem(attr.name);
+ }
+ });
+ slice(root.children).forEach(removeListeners);
+ };
+
var hjsonToDom = function (H) {
- return Hyperjson.toDOM(H); //callOn(H, Hyperscript);
+ var dom = Hyperjson.toDOM(H);
+ removeListeners(dom);
+ return dom;
};
var module = window.REALTIME_MODULE = window.APP = {
@@ -321,9 +336,9 @@ define([
})) { return text; }
};
- var suggestName = function () {
+ var suggestName = function (fallback) {
if (document.title === defaultName) {
- return getHeadingText() || "";
+ return getHeadingText() || fallback || "";
} else {
return document.title || getHeadingText() || defaultName;
}
@@ -510,7 +525,7 @@ define([
var exportFile = function () {
var html = getHTML();
- var suggestion = suggestName();
+ var suggestion = suggestName('cryptpad-document');
Cryptpad.prompt(Messages.exportPrompt,
Cryptpad.fixFileName(suggestion) + '.html', function (filename) {
if (!(typeof(filename) === 'string' && filename)) { return; }
@@ -601,9 +616,7 @@ define([
}
// set the hash
- if (!readOnly) {
- window.location.hash = editHash;
- }
+ if (!readOnly) { Cryptpad.replaceHash(editHash); }
Cryptpad.getPadTitle(function (err, title) {
if (err) {
diff --git a/www/poll/main.js b/www/poll/main.js
index bbe3fc97b..7417ad10b 100644
--- a/www/poll/main.js
+++ b/www/poll/main.js
@@ -72,6 +72,23 @@ define([
});
};
+ var getLastName = module.getLastName = function (cb) {
+ Cryptpad.getAttribute('username', function (err, userName) {
+ cb(err, userName || '');
+ });
+ };
+
+ var setName = module.setName = function (uname, cb) {
+ if (typeof(uname) !== 'string') {
+ return void cb(new Error('expected string'));
+ }
+ uname = Cryptpad.fixHTML(uname.trim()).slice(0, 32);
+ Cryptpad.setAttribute('username', uname, function (err, data) {
+ if (err) { return void cb(err); }
+ cb(void 0, uname);
+ });
+ };
+
module.Wizard = Wizard;
// special UI elements
@@ -320,12 +337,19 @@ define([
};
var scrollDown = module.scrollDown = function (px) {
+ if (module.scrolling) { return; }
+
+ module.scrolling = true;
+
var top = $(window).scrollTop() + px + 'px';
$('html, body').animate({
scrollTop: top,
}, {
duration: 200,
easing: 'swing',
+ complete: function () {
+ module.scrolling = false;
+ }
});
};
@@ -397,7 +421,10 @@ define([
addIfAbsent(proxy.table.rowsOrder, id);
var $row = table.addRow($wrapper, Checkbox, id);
- scrollDown($row.height());
+
+ if (module.ready) {
+ scrollDown($row.height());
+ }
return $option;
};
@@ -408,7 +435,7 @@ define([
var msg = Messages.poll_addUser;
Cryptpad.prompt(msg, "", function (name) {
- if (name === null) { return; }
+ if (!(name && name.trim())) { return; }
makeUser(module.rt.proxy, id, name).val(name);
makeUserEditable(id, true).focus();
});
@@ -439,7 +466,7 @@ define([
var id = rowuid();
makeOption(proxy, id, text).val(text);
});
- //console.log(options);
+ Wizard.hide();
});
});
@@ -755,14 +782,16 @@ define([
}));
}
+/*
if (!readOnly && module.viewHash) {
- /* add a 'links' button */
+ /* add a 'links' button
var $links = Cryptpad.createButton('readonly', true, {viewHash: module.viewHash})
.text(Messages.getViewButton)
.removeAttr('style')
.attr('class', 'action button readonly');
$toolbar.append($links);
}
+*/
/* Import/Export buttons */
/*
@@ -815,7 +844,11 @@ define([
setEditable(true);
+ return;
+ // shortcircuiting before all of this code since it's not quite the
+ // behaviour we want, and it's a bit of work to make it Do The Right Thing
+/*
if (First) {
// assume the first user to the poll wants to be the administrator...
// TODO prompt them with questions to set up their poll...
@@ -830,40 +863,34 @@ define([
module.activeColumn = '';
var promptForName = function () {
- // HERE
- Cryptpad.prompt(Messages.promptName, "", function (name, ev) {
- if (name === null) {
- name = '';
- }
- if (!module.isEditable) { return; }
+ var followUp = function (name) {
+ if (!name) { return; }
var id = module.activeColumn = coluid();
Cryptpad.setPadAttribute('column', id, function (err) {
- if (err) {
- console.error("Couldn't remember your column id");
- return;
- }
-
- console.log(id);
+ if (err) { return void console.error("Couldn't remember your column id"); }
makeUser(module.rt.proxy, id, name).focus().val(name);
makeUserEditable(id, true);
});
+ };
+
+ getLastName(function (err, uname) {
+ if (!uname) {
+ return void Cryptpad.prompt(Messages.promptName, "", function (name, ev) {
+ if (!(name || module.isEditable)) { return; }
+ followUp(name);
+ });
+ }
+ followUp(uname);
});
};
- if (column === null) {
- //console.log("Looks like you're new to this poll, why don't you make a column");
- promptForName();
- return;
- }
+ if (column === null) { return void promptForName(); }
// column might be defined, but that column might have been deleted...
- if (proxy.table.colsOrder.indexOf(column) === -1) {
- promptForName();
- return;
- }
- });
+ if (proxy.table.colsOrder.indexOf(column) === -1) { return void promptForName(); }
+ });*/
};
var config = {
@@ -889,9 +916,7 @@ define([
editHash = Cryptpad.getEditHashFromKeys(info.channel, secret.keys);
}
// set the hash
- if (!readOnly) {
- window.location.hash = editHash;
- }
+ if (!readOnly) { Cryptpad.replaceHash(editHash); }
module.patchText = TextPatcher.create({
realtime: realtime,
diff --git a/www/slide/main.js b/www/slide/main.js
index 91a174d0f..a291ae02c 100644
--- a/www/slide/main.js
+++ b/www/slide/main.js
@@ -585,7 +585,7 @@ define([
// set the hash
if (!window.location.hash || window.location.hash === '#') {
- window.location.hash = editHash;
+ Cryptpad.replaceHash(editHash);
}
Cryptpad.getPadTitle(function (err, title) {
diff --git a/www/slide/slide.js b/www/slide/slide.js
index b07c1b63d..cae81204f 100644
--- a/www/slide/slide.js
+++ b/www/slide/slide.js
@@ -45,7 +45,7 @@ define([
var unsafeTag = function (info) {
if (['addAttribute', 'modifyAttribute'].indexOf(info.diff.action) !== -1) {
if (/^on/.test(info.diff.name)) {
- console.log("Rejecting forbidden element attribute with name", info.diff.element.nodeName);
+ console.log("Rejecting forbidden element attribute with name", info.diff.name);
return true;
}
}
@@ -91,12 +91,28 @@ define([
return patch;
};
+ var slice = function (coll) {
+ return Array.prototype.slice.call(coll);
+ };
+
+ /* remove listeners from the DOM */
+ var removeListeners = function (root) {
+ slice(root.attributes).map(function (attr) {
+ if (/^on/.test(attr.name)) {
+ root.attributes.removeNamedItem(attr.name);
+ }
+ });
+ // all the way down
+ slice(root.children).forEach(removeListeners);
+ };
+
var draw = Slide.draw = function (i) {
console.log("Trying to draw slide #%s", i);
if (typeof(Slide.content[i]) !== 'string') { return; }
var c = Slide.content[i];
var Dom = domFromHTML('
' + Marked(c) + '
');
+ removeListeners(Dom.body);
var patch = makeDiff(domFromHTML($content[0].outerHTML), Dom);
if (typeof(patch) === 'string') {