├── index.js ├── img ├── lang-de.png ├── lang-en.png ├── lang-es.png ├── lang-it.png ├── lang-pt.png ├── lang-ru.png ├── comment-close.png ├── comment-open.png ├── flag-lang-de.png ├── flag-lang-en.png ├── flag-lang-es.png ├── flag-lang-it.png ├── flag-lang-jpn.png ├── flag-lang-pt.png ├── flag-lang-ru.png ├── flag-lang-de-100.png ├── flag-lang-en-100.png ├── flag-lang-es-100.png ├── flag-lang-it-100.png ├── flag-lang-pt-100.png └── flag-lang-ru-100.png ├── .vscode ├── settings.json └── launch.json ├── .jshintrc ├── .travis.yml ├── langs ├── lang-it.yaml ├── lang-ja.yaml ├── lang-ru.yaml ├── lang-de.yaml ├── lang-es.yaml └── lang-fr.yaml ├── appveyor.yml ├── .gitignore ├── examples ├── README.md ├── con-espacios.md ├── мультиязычный.md ├── multilenguaje.md ├── multilingua.md ├── mehrsprachig.md ├── with-spaces.md ├── from-es.md ├── desde-es.md └── multilanguage.md ├── LICENSE ├── package.json ├── bin ├── multilang-run.js └── multilang.js ├── test ├── test-coverage.js ├── test-main.js └── test.js ├── LIESMICH.md ├── README.md ├── .eslintrc.yml └── LEEME.md /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = require('./bin/multilang.js'); -------------------------------------------------------------------------------- /img/lang-de.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/lang-de.png -------------------------------------------------------------------------------- /img/lang-en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/lang-en.png -------------------------------------------------------------------------------- /img/lang-es.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/lang-es.png -------------------------------------------------------------------------------- /img/lang-it.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/lang-it.png -------------------------------------------------------------------------------- /img/lang-pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/lang-pt.png -------------------------------------------------------------------------------- /img/lang-ru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/lang-ru.png -------------------------------------------------------------------------------- /img/comment-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/comment-close.png -------------------------------------------------------------------------------- /img/comment-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/comment-open.png -------------------------------------------------------------------------------- /img/flag-lang-de.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/flag-lang-de.png -------------------------------------------------------------------------------- /img/flag-lang-en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/flag-lang-en.png -------------------------------------------------------------------------------- /img/flag-lang-es.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/flag-lang-es.png -------------------------------------------------------------------------------- /img/flag-lang-it.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/flag-lang-it.png -------------------------------------------------------------------------------- /img/flag-lang-jpn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/flag-lang-jpn.png -------------------------------------------------------------------------------- /img/flag-lang-pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/flag-lang-pt.png -------------------------------------------------------------------------------- /img/flag-lang-ru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/flag-lang-ru.png -------------------------------------------------------------------------------- /img/flag-lang-de-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/flag-lang-de-100.png -------------------------------------------------------------------------------- /img/flag-lang-en-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/flag-lang-en-100.png -------------------------------------------------------------------------------- /img/flag-lang-es-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/flag-lang-es-100.png -------------------------------------------------------------------------------- /img/flag-lang-it-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/flag-lang-it-100.png -------------------------------------------------------------------------------- /img/flag-lang-pt-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/flag-lang-pt-100.png -------------------------------------------------------------------------------- /img/flag-lang-ru-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenautas/multilang/HEAD/img/flag-lang-ru-100.png -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "window.title": "🔨 ${dirty}${separator}${rootName}${separator}${activeEditorMedium}" 3 | } -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "asi": false, 3 | "forin": true, 4 | "curly": true, 5 | "node": true, 6 | "strict": "global", 7 | "eqnull": true, 8 | "predef": [ "module", "Promise" ] 9 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "12" 4 | - "14" 5 | - "16" 6 | matrix: 7 | fast_finish: true 8 | script: "npm run-script test-ci" 9 | after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls" 10 | -------------------------------------------------------------------------------- /langs/lang-it.yaml: -------------------------------------------------------------------------------- 1 | name: italiano 2 | abr: it 3 | languages: 4 | de: tedesco 5 | en: inlgese 6 | es: spagnolo 7 | fr: francese 8 | it: italiano 9 | ru: russo 10 | ja: giapponese 11 | phrases: 12 | language: lingua 13 | also available in: disponibile anche in -------------------------------------------------------------------------------- /langs/lang-ja.yaml: -------------------------------------------------------------------------------- 1 | name: 日本語 2 | abr: ja 3 | languages: 4 | de: ドイツ語 5 | en: 英語 6 | es: スペイン語 7 | fr: フランス語 8 | it: イタリア語 9 | ru: ロシア語 10 | ja: 日本語 11 | phrases: 12 | language: 言語 13 | also available in: "次の言語で利用可能: " 14 | DO NOT MODIFY DIRECTLY: "multilang.jsを直接編集しないでください" 15 | -------------------------------------------------------------------------------- /langs/lang-ru.yaml: -------------------------------------------------------------------------------- 1 | name: русский 2 | abr: ru 3 | languages: 4 | de: по-неме́цки 5 | en: английский 6 | es: испанский 7 | fr: францу́зский 8 | it: итальянский 9 | ru: русский 10 | ja: Японский 11 | phrases: 12 | language: язык 13 | also available in: также доступны в 14 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: "14" 4 | - nodejs_version: "16" 5 | matrix: 6 | fast_finish: true 7 | install: 8 | - ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) 9 | - npm install 10 | build: off 11 | test_script: 12 | - node --version 13 | - npm --version 14 | - npm run test-ci 15 | version: "{build}" 16 | -------------------------------------------------------------------------------- /langs/lang-de.yaml: -------------------------------------------------------------------------------- 1 | name: Deutsch 2 | abr: de 3 | languages: 4 | de: Deutsch 5 | en: Englisch 6 | es: Spanisch 7 | fr: Französisch 8 | it: Italienisch 9 | ru: Russisch 10 | ja: japanisch 11 | phrases: 12 | language: Sprache 13 | also available in: ebenfalls verfügbar in 14 | DO NOT MODIFY DIRECTLY: Bitte nicht direkt ändern, das es sich um generierte Inhalte multilang.js handelt! 15 | -------------------------------------------------------------------------------- /langs/lang-es.yaml: -------------------------------------------------------------------------------- 1 | name: castellano 2 | abr: es 3 | languages: 4 | de: alemán 5 | en: inglés 6 | es: español 7 | fr: francés 8 | it: italiano 9 | ru: ruso 10 | ja: japonés 11 | phrases: 12 | language: idioma 13 | also available in: también disponible en 14 | DO NOT MODIFY DIRECTLY: NO MODIFIQUE ESTE ARCHIVO. FUE GENERADO AUTOMÁTICAMENTE POR multilang.js #no se refiere a éste sino al generado 15 | -------------------------------------------------------------------------------- /langs/lang-fr.yaml: -------------------------------------------------------------------------------- 1 | name: français 2 | abr: fr 3 | languages: 4 | de: allemand 5 | en: anglais 6 | es: espagnol 7 | fr: français 8 | it: italiano 9 | ru: russe 10 | ja: Japonais 11 | phrases: 12 | language: langue 13 | also available in: également disponible en 14 | DO NOT MODIFY DIRECTLY: NE MODIFIEZ PAS DIRECTEMENT CE DOSSIER. A été généré par multilang.js #no se refiere a éste sino al generado 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | 30 | local-* 31 | *-local.* 32 | 33 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | language: ![English](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png) 5 | also available in: 6 | [Spanish](LEEME.md) [French](LISEZMOI.md) [German](LIESMICH.md) 7 | 8 | 9 | This is a little example 10 | 17 | 18 | 19 | "*" means all languages 20 | 27 | All you need is multilang! -------------------------------------------------------------------------------- /examples/con-espacios.md: -------------------------------------------------------------------------------- 1 | 12 | # Multilanguage 13 | 14 | This is an example of multilanguage markdown file. 15 | 16 | 17 | ![designing](https://img.shields.io/badge/stability-designing-orange.svg) 18 | ![extending](https://img.shields.io/badge/stability-extending-orange.svg) 19 | ![inventing](https://img.shields.io/badge/stability-inventing-orange.svg) 20 | 21 | 22 | 23 | idioma: ![castellano](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png) 24 | también disponible en: 25 | [![inglés](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png)](with-spaces.md) 26 | 27 | 28 | Esto es una prueba de archivos markdown multilenguajes. 29 | 30 | El principal objetivo es escribir la documentación en un único fuente. 31 | 32 | 33 | # Licencia 34 | 35 | 36 | MIT 37 | -------------------------------------------------------------------------------- /examples/мультиязычный.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | язык: ![русский](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-ru.png) 15 | также доступны в: 16 | [![английский](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png)](multilanguage.md) - 17 | [![испанский](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png)](multilenguaje.md) - 18 | [![итальянский](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-it.png)](multilingua.md) - 19 | [![по-неме́цки](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-de.png)](mehrsprachig.md) 20 | 21 | # мультиязычный (Multilanguage) 22 | 23 | Этопример многоязычной файла уценки. 24 | 25 | Основная цель написать мультиязычный документации в единой источника. 26 | 27 | # лицензия 28 | 29 | MIT 30 | -------------------------------------------------------------------------------- /examples/multilenguaje.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | idioma: ![castellano](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png) 15 | también disponible en: 16 | [![inglés](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png)](multilanguage.md) - 17 | [![italiano](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-it.png)](multilingua.md) - 18 | [![ruso](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-ru.png)](мультиязычный.md) - 19 | [![alemán](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-de.png)](mehrsprachig.md) 20 | 21 | # Multilenguaje (Multilanguage) 22 | 23 | Esto es una prueba de archivos markdown multilenguajes. 24 | 25 | El principal objetivo es escribir la documentación en un único fuente. 26 | 27 | # Licencia 28 | 29 | MIT 30 | -------------------------------------------------------------------------------- /examples/multilingua.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | lingua: ![italiano](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-it.png) 15 | disponibile anche in: 16 | [![inlgese](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png)](multilanguage.md) - 17 | [![spagnolo](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png)](multilenguaje.md) - 18 | [![russo](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-ru.png)](мультиязычный.md) - 19 | [![tedesco](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-de.png)](mehrsprachig.md) 20 | 21 | # Multilingua (Multilanguage) 22 | 23 | Questo è un esempio di file di Markdown multilingua. 24 | 25 | L'obiettivo principale è scrivere la documentazione multilingue in una fonte unificata. 26 | 27 | # Licenza 28 | 29 | MIT 30 | -------------------------------------------------------------------------------- /examples/mehrsprachig.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | Sprache: ![Deutsch](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-de.png) 15 | ebenfalls verfügbar in: 16 | [![Englisch](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png)](multilanguage.md) - 17 | [![Spanisch](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png)](multilenguaje.md) - 18 | [![Italienisch](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-it.png)](multilingua.md) - 19 | [![Russisch](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-ru.png)](мультиязычный.md) 20 | 21 | # Mehrsprachigkeit 22 | 23 | Dies ist ein Beispiel für eine mehrsprachige Markdown-Datei. 24 | 25 | Das Hauptziel ist es, mehrsprachige Dokumentation einheitlich zu schreiben. 26 | 27 | # Lizenz 28 | 29 | MIT 30 | -------------------------------------------------------------------------------- /examples/with-spaces.md: -------------------------------------------------------------------------------- 1 | # Multilanguage 2 | 3 | This is an example of multilanguage markdown file. 4 | 5 | 6 | 7 | ![designing](https://img.shields.io/badge/stability-designing-orange.svg) 8 | ![extending](https://img.shields.io/badge/stability-extending-orange.svg) 9 | ![inventing](https://img.shields.io/badge/stability-inventing-orange.svg) 10 | 11 | 12 | 13 | language: ![English](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png) 14 | also available in: 15 | [![Spanish](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png)](con-espacios.md) 16 | 17 | 18 | 19 | The main goal is write multilanguage documentation in an unified source. 20 | 21 | 28 | 29 | # License 30 | 31 | 36 | 37 | MIT 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Codenautas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /examples/from-es.md: -------------------------------------------------------------------------------- 1 | 12 | # qa-control 13 | 14 | Quality assurance tool for Node projects 15 | 16 | 17 | 18 | ![designing](https://img.shields.io/badge/stability-desgining-red.svg) 19 | [![npm-version](https://img.shields.io/npm/v/qa-control.svg)](https://npmjs.org/package/qa-control) 20 | [![downloads](https://img.shields.io/npm/dm/qa-control.svg)](https://npmjs.org/package/qa-control) 21 | [![build](https://img.shields.io/travis/codenautas/qa-control/master.svg)](https://travis-ci.org/codenautas/qa-control) 22 | [![coverage](https://img.shields.io/coveralls/codenautas/qa-control/master.svg)](https://coveralls.io/r/codenautas/qa-control) 23 | [![dependencies](https://img.shields.io/david/codenautas/qa-control.svg)](https://david-dm.org/codenautas/qa-control) 24 | 25 | 26 | 27 | 28 | language: ![English](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png) 29 | also available in: 30 | [![Spanish](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png)](desde-es.md) 31 | 32 | 33 | ## Install 34 | 35 | 36 | ```sh 37 | $ npm install -g qa-control 38 | ``` 39 | 40 | 41 | ## Usage 42 | 43 | 44 | ```sh 45 | $ pwd 46 | /home/user/npm-packages/this-module 47 | ``` 48 | 49 | ## License 50 | 51 | [MIT](LICENSE) 52 | 53 | ---------------- 54 | 55 | 56 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | // Name of configuration; appears in the launch configuration drop down menu. 9 | "name": "Run mocha", 10 | "request": "launch", 11 | // Type of configuration. Possible values: "node", "mono". 12 | "type": "node", 13 | // Workspace relative or absolute path to the program. 14 | "x-preLaunchTask": "tsc", 15 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", 16 | // Automatically stop program after launch. 17 | "stopOnEntry": false, 18 | // Command line arguments passed to the program. 19 | "args": ["test/*.js"], 20 | // Workspace relative or absolute path to the working directory of the program being debugged. Default is the current workspace. 21 | "cwd": "${workspaceFolder}", 22 | // Workspace relative or absolute path to the runtime executable to be used. Default is the runtime executable on the PATH. 23 | "runtimeExecutable": null, 24 | // Environment variables passed to the program. 25 | "env": { "NODE_ENV": "testing"}, 26 | "outFiles": [ 27 | "${workspaceFolder}/lib/**/*.js" 28 | ] 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /examples/desde-es.md: -------------------------------------------------------------------------------- 1 | # qa-control 2 | 3 | 4 | Herramienta de control de calidad para proyectos Node 5 | 6 | 10 | 11 | 12 | ![designing](https://img.shields.io/badge/stability-desgining-red.svg) 13 | [![npm-version](https://img.shields.io/npm/v/qa-control.svg)](https://npmjs.org/package/qa-control) 14 | [![downloads](https://img.shields.io/npm/dm/qa-control.svg)](https://npmjs.org/package/qa-control) 15 | [![build](https://img.shields.io/travis/codenautas/qa-control/master.svg)](https://travis-ci.org/codenautas/qa-control) 16 | [![coverage](https://img.shields.io/coveralls/codenautas/qa-control/master.svg)](https://coveralls.io/r/codenautas/qa-control) 17 | [![dependencies](https://img.shields.io/david/codenautas/qa-control.svg)](https://david-dm.org/codenautas/qa-control) 18 | 19 | 20 | 21 | 22 | 23 | idioma: ![castellano](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png) 24 | también disponible en: 25 | [![inglés](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png)](README.md) 26 | 27 | 28 | 29 | ## Instalación 30 | 31 | 36 | 37 | ```sh 38 | $ npm install -g qa-control 39 | ``` 40 | 41 | 42 | 43 | ## Uso 44 | 45 | 50 | 51 | ```sh 52 | $ pwd 53 | /home/user/npm-packages/this-module 54 | ``` 55 | 56 | ## License 57 | 58 | [MIT](LICENSE) 59 | 60 | ---------------- 61 | 62 | 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multilang", 3 | "description": "Tools for multilanguage and Markdown multilang", 4 | "version": "1.2.0", 5 | "author": "Codenautas ", 6 | "license": "MIT", 7 | "repository": "https://github.com/codenautas/multilang", 8 | "contributors": [ 9 | { 10 | "name": "Emilio Platzer", 11 | "email": "emilioplatzer@gmail.com" 12 | }, 13 | { 14 | "name": "Diego Florio", 15 | "email": "diegoefe@gmail.com" 16 | } 17 | ], 18 | "bin": { 19 | "multilang": "./bin/multilang-run.js" 20 | }, 21 | "main": "./bin/multilang.js", 22 | "dependencies": { 23 | "commander": "9.4.0", 24 | "fs-promise": "~2.0.3", 25 | "js-yaml": "4.1.0", 26 | "strip-bom-string": "~1.0.0", 27 | "best-globals": "~1.0.3" 28 | }, 29 | "devDependencies": { 30 | "expect.js": "~0.3.1", 31 | "istanbul": "~0.4.5", 32 | "mocha": "~10.0.0", 33 | "expect-called": "~0.4.0" 34 | }, 35 | "engines": { 36 | "node": ">= 6" 37 | }, 38 | "scripts": { 39 | "test": "mocha --reporter spec --bail --check-leaks test/", 40 | "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/", 41 | "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", 42 | "qac": "qa-control . -v", 43 | "all": "npm test && npm run test-cov && npm run qac", 44 | "start": "node example/server.js" 45 | }, 46 | "files": [ 47 | "bin", 48 | "langs" 49 | ], 50 | "qa-control": { 51 | "package-version": "0.3.0", 52 | "coverage": 99, 53 | "run-in": "server", 54 | "stability": "extending", 55 | "type": "cmd-tool" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /examples/multilanguage.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | language: ![English](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png) 5 | also available in: 6 | [![Spanish](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png)](multilenguaje.md) - 7 | [![Italian](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-it.png)](multilingua.md) - 8 | [![Russian](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-ru.png)](мультиязычный.md) 9 | [![German](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-de.png)](mehrsprachig.md) 10 | 11 | 12 | # Multilanguage 13 | 14 | This is an example of multilanguage markdown file. 15 | 16 | The main goal is write multilanguage documentation in an unified source. 17 | 18 | 47 | # License 48 | 49 | 62 | MIT 63 | 64 | -------------------------------------------------------------------------------- /bin/multilang-run.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | var program = require('commander'); 6 | var multilang = require('./multilang'); 7 | var fs = require('fs-promise'); 8 | var path = require('path'); 9 | 10 | function realPath(inFile) { 11 | return Promise.resolve().then(function() { 12 | if(!inFile) { throw new Error("null file"); } 13 | return fs.exists(inFile); 14 | }).then(function(exists) { 15 | if(! exists) { throw new Error("'"+inFile+"' does not exists"); } 16 | return inFile; 17 | }).then(function(inFile) { 18 | return path.dirname(path.resolve(inFile)); 19 | }).catch(function(err) { 20 | return Promise.reject(err); 21 | }); 22 | }; 23 | 24 | function langs(val) { 25 | return val.split(',') 26 | } 27 | 28 | program 29 | .version(require('../package').version) 30 | .usage('[options] input.md') 31 | .option('-i, --input [input.md]', 'Name of the input file') 32 | .option('-l, --lang [lang1]', 'Language to generate', langs) 33 | .option('-o, --output [name]', 'Name of the output file. Requires --langs!') 34 | .option('-d, --directory [name]', 'Name of the output directory.') 35 | .option('-c, --check', 'Run multilang generating no files') 36 | .option('-s, --silent', 'Do not output anything') 37 | .option('--strip-comments', 'Remove HTML comments from output') 38 | .option('--no-strip-comments', 'Do not remove HTML comments from output') 39 | .option('-v, --verbose', 'Output all progress informations') 40 | .parse(process.argv); 41 | 42 | 43 | function isLongOptionSet(ame) { 44 | var a=program.rawArgs; 45 | for(var e=0; e 12 | # multilang 13 | 14 | Werkzeug für Mehrsprachigkeit & Markdown multilang 15 | 16 | 17 | 18 | [![npm-version](https://img.shields.io/npm/v/multilang.svg)](https://npmjs.org/package/multilang) 19 | [![downloads](https://img.shields.io/npm/dm/multilang.svg)](https://npmjs.org/package/multilang) 20 | [![build](https://img.shields.io/travis/codenautas/multilang/master.svg)](https://travis-ci.org/codenautas/multilang) 21 | [![coverage](https://img.shields.io/coveralls/codenautas/multilang/master.svg)](https://coveralls.io/r/codenautas/multilang) 22 | 23 | 24 | 25 | Sprache: ![Deutsch](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-de.png) 26 | ebenfalls verfügbar in: 27 | [![Spanisch](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png)](LEEME.md) - 28 | [![Englisch](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png)](README.md) 29 | 30 | 31 | ## Installation 32 | 33 | 34 | ```sh 35 | $ npm install multilang -g 36 | ``` 37 | 38 | 39 | ## Nutzung 40 | 41 | 42 | ``` 43 | $ multilang doc-en.md 44 | ``` 45 | 46 | 47 | Es wird jeweils eine .md Datei pro Sprache aus `doc-en.md` erzeugt. 48 | 49 | 50 | ## Mehrsprachiges Dokumentenformat 51 | 52 | Jedes HTML oder Markdown Dokument ist ein mehrsprachiges Dokument, 53 | wenn es eine *multilanguage* Direktive beinhaltet. 54 | 55 | ### Beispiel 56 | 57 | 58 | ``` 59 | 60 | 61 | 62 | language: ![English](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png) 63 | also available in: 64 | [Spanish](LEEME.md) [French](LISEZMOI.md) [German](LIESMICH.md) 65 | 66 | 67 | This is a little example 68 | 75 | 76 | 77 | "*" means all languages 78 | 85 | All you need is multilang! 86 | ``` 87 | 88 | 89 | In diesem Beispiel: 90 | 91 | 92 | ``` 93 | 94 | ``` 95 | 96 | 97 | ist die Direktive zur Deklaration der Sprache 98 | 99 | 100 | ``` 101 | 102 | ``` 103 | 104 | 105 | ist die Direktive zur Deklaration der Platzierung für die Button-Section 106 | 107 | 108 | ``` 109 | [!--lang:fr--] 110 | ``` 111 | 112 | 113 | ist die Direktive zur Deklaration der Sprache für die nächste Section (der * gilt für alle Sprachen) 114 | 115 | 116 | ## API 117 | 118 | ```js 119 | 120 | var fs = require('fs'); 121 | var multilang = require('multilang'); 122 | 123 | var englishText = fs.readFileSync('README.md', {encoding:'utf8'}); 124 | 125 | var warnings = multilang.getWarnings(englishText); 126 | if(warnings.lengt){ 127 | console.log('WARN', warnings); 128 | } 129 | 130 | var spanishText = multilang.changeDoc(englishText,'es'); 131 | 132 | console.log('spanish.md',spanishText); 133 | ``` 134 | 135 | 136 | (Hinweis zum Beispiel: nutzen Sie die ***Sync*** Funktionen nicht produktiv, 137 | sondern nutzen Sie async 138 | oder [promise](http://npmjs.com/package/fs-promise) version 139 | siehe [codenautas](https://github.com/codenautas/codenautas/blob/master/examples/promises.md) 140 | 141 | Funktion | Nutzung 142 | ---------------------|------------------------------ 143 | changeDoc(text,lang) | nutzt einen zu übersetzenden Text und die Zielsprache als Code und gibt den übersetzten Text zurück 144 | warnings(text) | nutzt eine Liste von Warnungen und gibt einen übersetzten Text zurück 145 | 146 | 147 | ## License 148 | 149 | [MIT](LICENSE) 150 | 151 | ................................... 152 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # multilang 2 | 3 | Tools for multilanguage & Markdown multilang 4 | 5 | 6 | [![npm-version](https://img.shields.io/npm/v/multilang.svg)](https://npmjs.org/package/multilang) 7 | [![downloads](https://img.shields.io/npm/dm/multilang.svg)](https://npmjs.org/package/multilang) 8 | [![build](https://img.shields.io/travis/codenautas/multilang/master.svg)](https://travis-ci.org/codenautas/multilang) 9 | [![coverage](https://img.shields.io/coveralls/codenautas/multilang/master.svg)](https://coveralls.io/r/codenautas/multilang) 10 | 11 | 12 | language: ![English](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png) 13 | also available in: 14 | [![Spanish](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png)](LEEME.md) - 15 | [![German](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-de.png)](LIESMICH.md) 16 | 17 | 18 | In a Markdown or HTML-like file is written the documentation in multiple languages. 19 | 20 | One of these languages ​​is the main, the others are commented with ![open](https://raw.githubusercontent.com/codenautas/multilang/master/img/comment-open.png) and ![close](https://raw.githubusercontent.com/codenautas/multilang/master/img/comment-close.png) 21 | 22 | Then with ` multilang` the other languages ​​are extracted to generate one file for each of the other languages ​​defined 23 | 24 | 25 | ## Install 26 | 27 | 28 | ```sh 29 | $ npm install multilang -g 30 | ``` 31 | 32 | 33 | ## How to use 34 | 35 | 36 | ``` 37 | $ multilang doc-en.md 38 | ``` 39 | 40 | 41 | A .md file is generated for the other languages written in `doc-en.md` file 42 | 43 | 44 | ## Multilanguage document format 45 | 46 | Any HTML or Markdown document is a multilenguage document if it has a main *multilanguage* directive. 47 | 48 | ### Example 49 | 50 | 51 | ``` 52 | 53 | 54 | 55 | language: ![English](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png) 56 | also available in: 57 | [Spanish](LEEME.md) [French](LISEZMOI.md) [German](LIESMICH.md) 58 | 59 | 60 | This is a little example 61 | 68 | 69 | 70 | "*" means all languages 71 | 78 | All you need is multilang! 79 | ``` 80 | 81 | 82 | In this example: 83 | 84 | 85 | ``` 86 | 87 | ``` 88 | 89 | 90 | is the directive for declare the languages 91 | 92 | 93 | ``` 94 | 95 | ``` 96 | 97 | 98 | is the directive for declaring the place for the button section 99 | 100 | 101 | ``` 102 | [!--lang:fr--] 103 | ``` 104 | 105 | 106 | is the directive for declaring the language of the next section (use * for all languages) 107 | 108 | 109 | ## API 110 | 111 | ```js 112 | 113 | var fs = require('fs'); 114 | var multilang = require('multilang'); 115 | 116 | var englishText = fs.readFileSync('README.md', {encoding:'utf8'}); 117 | 118 | var warnings = multilang.getWarnings(englishText); 119 | if(warnings.lengt){ 120 | console.log('WARN', warnings); 121 | } 122 | 123 | var spanishText = multilang.changeDoc(englishText,'es'); 124 | 125 | console.log('spanish.md',spanishText); 126 | ``` 127 | 128 | 129 | (note about the example: do not use ***Sync*** functions in production, 130 | use async 131 | or [promise](http://npmjs.com/package/fs-promise) version 132 | as you can see in [codenautas](https://github.com/codenautas/codenautas/blob/master/examples/promises.md) 133 | 134 | function | use 135 | ---------------------|------------------------------ 136 | changeDoc(text,lang) | receives a multilang text and a language code and returns de text of specified lang 137 | warnings(text) | receives a list of warnings and returns a multilang text 138 | 139 | 140 | ## License 141 | 142 | [MIT](LICENSE) 143 | 144 | ................................... 145 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | ecmaFeatures: 2 | modules: true 3 | jsx: true 4 | 5 | env: 6 | amd: true 7 | browser: true 8 | es6: true 9 | jquery: true 10 | node: true 11 | 12 | # http://eslint.org/docs/rules/ 13 | rules: 14 | # Possible Errors 15 | comma-dangle: 0 16 | no-cond-assign: 2 17 | no-console: 0 18 | no-constant-condition: 2 19 | no-control-regex: 2 20 | no-debugger: 2 21 | no-dupe-args: 2 22 | no-dupe-keys: 2 23 | no-duplicate-case: 2 24 | no-empty: 2 25 | no-empty-character-class: 2 26 | no-ex-assign: 2 27 | no-extra-boolean-cast: 2 28 | no-extra-parens: 0 29 | no-extra-semi: 2 30 | no-func-assign: 2 31 | no-inner-declarations: [2, functions] 32 | no-invalid-regexp: 2 33 | no-irregular-whitespace: 2 34 | no-negated-in-lhs: 2 35 | no-obj-calls: 2 36 | no-regex-spaces: 2 37 | no-sparse-arrays: 2 38 | no-unexpected-multiline: 2 39 | no-unreachable: 2 40 | use-isnan: 2 41 | valid-jsdoc: 0 42 | valid-typeof: 2 43 | 44 | # Best Practices 45 | accessor-pairs: 2 46 | block-scoped-var: 0 47 | complexity: [1, 24] 48 | consistent-return: 0 49 | curly: 0 50 | default-case: 0 51 | dot-location: 0 52 | dot-notation: 0 53 | # OJO eqeqeq 54 | eqeqeq: 0 55 | guard-for-in: 2 56 | no-alert: 2 57 | no-caller: 2 58 | no-case-declarations: 2 59 | no-div-regex: 2 60 | no-else-return: 0 61 | no-empty-pattern: 2 62 | no-eq-null: 0 63 | no-eval: 2 64 | no-extend-native: 2 65 | no-extra-bind: 2 66 | no-fallthrough: 2 67 | no-floating-decimal: 0 68 | no-implicit-coercion: 0 69 | no-implied-eval: 2 70 | no-invalid-this: 0 71 | no-iterator: 2 72 | no-labels: 0 73 | no-lone-blocks: 2 74 | no-loop-func: 2 75 | no-magic-number: 0 76 | no-multi-spaces: 0 77 | no-multi-str: 0 78 | no-native-reassign: 2 79 | no-new-func: 2 80 | no-new-wrappers: 2 81 | no-new: 2 82 | no-octal-escape: 2 83 | no-octal: 2 84 | no-proto: 2 85 | no-redeclare: 2 86 | no-return-assign: 2 87 | no-script-url: 2 88 | no-self-compare: 2 89 | no-sequences: 0 90 | no-throw-literal: 0 91 | no-unused-expressions: 2 92 | no-useless-call: 2 93 | no-useless-concat: 2 94 | no-void: 2 95 | no-warning-comments: 0 96 | no-with: 2 97 | radix: 2 98 | vars-on-top: 0 99 | wrap-iife: [2, "inside"] 100 | yoda: 0 101 | 102 | # Strict 103 | strict: 2 #error 104 | 105 | # Variables 106 | init-declarations: 0 107 | no-catch-shadow: 2 108 | no-delete-var: 2 109 | no-label-var: 2 110 | no-shadow-restricted-names: 2 111 | no-shadow: 0 112 | no-undef-init: 2 113 | no-undef: 0 114 | no-undefined: 0 115 | no-unused-vars: 0 116 | no-use-before-define: 0 117 | 118 | # Node.js and CommonJS 119 | callback-return: 2 120 | global-require: 2 121 | handle-callback-err: 2 122 | no-mixed-requires: 0 123 | no-new-require: 0 124 | no-path-concat: 2 125 | no-process-exit: 2 126 | no-restricted-modules: 0 127 | no-sync: 0 128 | 129 | # Stylistic Issues 130 | array-bracket-spacing: 0 131 | block-spacing: 0 132 | brace-style: 0 133 | camelcase: 0 134 | comma-spacing: 0 135 | comma-style: 0 136 | computed-property-spacing: 0 137 | consistent-this: 0 138 | eol-last: 0 139 | func-names: 0 140 | func-style: 0 141 | id-length: 0 142 | id-match: 0 143 | indent: 0 144 | jsx-quotes: 0 145 | key-spacing: 0 146 | linebreak-style: 0 147 | lines-around-comment: 0 148 | max-depth: 0 149 | max-len: 0 150 | max-nested-callbacks: 0 151 | max-params: 0 152 | max-statements: [2, 50] 153 | new-cap: 0 154 | new-parens: 0 155 | newline-after-var: 0 156 | no-array-constructor: 0 157 | no-bitwise: 0 158 | no-continue: 0 159 | no-inline-comments: 0 160 | no-lonely-if: 0 161 | no-mixed-spaces-and-tabs: 0 162 | no-multiple-empty-lines: 0 163 | no-negated-condition: 0 164 | no-nested-ternary: 0 165 | no-new-object: 0 166 | no-plusplus: 0 167 | no-restricted-syntax: 0 168 | no-spaced-func: 0 169 | no-ternary: 0 170 | no-trailing-spaces: 0 171 | no-underscore-dangle: 0 172 | no-unneeded-ternary: 0 173 | object-curly-spacing: 0 174 | one-var: 0 175 | operator-assignment: 0 176 | operator-linebreak: 0 177 | padded-blocks: 0 178 | quote-props: 0 179 | quotes: 0 180 | require-jsdoc: 0 181 | semi-spacing: 0 182 | semi: 0 183 | sort-vars: 0 184 | space-after-keywords: 0 185 | space-before-blocks: 0 186 | space-before-function-paren: 0 187 | space-before-keywords: 0 188 | space-in-parens: 0 189 | space-infix-ops: 0 190 | space-return-throw-case: 0 191 | space-unary-ops: 0 192 | spaced-comment: 0 193 | wrap-regex: 0 194 | 195 | # ECMAScript 6 196 | arrow-body-style: 0 197 | arrow-parens: 0 198 | arrow-spacing: 0 199 | constructor-super: 0 200 | generator-star-spacing: 0 201 | no-arrow-condition: 0 202 | no-class-assign: 0 203 | no-const-assign: 0 204 | no-dupe-class-members: 0 205 | no-this-before-super: 0 206 | no-var: 0 207 | object-shorthand: 0 208 | prefer-arrow-callback: 0 209 | prefer-const: 0 210 | prefer-reflect: 0 211 | prefer-spread: 0 212 | prefer-template: 0 213 | require-yield: 0 214 | -------------------------------------------------------------------------------- /test/test-main.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var expect = require('expect.js'); 4 | var fs = require('fs-promise'); 5 | var multilang = require('..'); 6 | var stripBom = require('strip-bom-string'); 7 | var expectCalled = require('expect-called'); 8 | var path = require('path'); 9 | 10 | 11 | function MiniStreamCapture(){ 12 | var internalContent = []; 13 | this.write = function write(message){ 14 | internalContent.push(message); 15 | } 16 | this.getContent = function getContent(){ 17 | return internalContent.join(''); 18 | } 19 | } 20 | 21 | describe('multilang.main', function(){ 22 | var contentOfDoc='The content of the doc'; 23 | function doSimpleSuccessTask(done,opts){ 24 | var obtainLangsControl=expectCalled.control(multilang,'obtainLangs',{returns:[ 25 | {main:'mm', langs:{xx:{fileName:'xx.md'}}}, 26 | {main:'mm', langs:{xx:{fileName:'xx.md'}}}, 27 | ]}); 28 | var readFileControl =expectCalled.control(fs,'readFile',{returns:[Promise.resolve(contentOfDoc)]}); 29 | var changeDocControl=expectCalled.control(multilang,'changeDoc',{returns:['valid content']}); 30 | var writeFileControl=expectCalled.control(fs,'writeFile',{returns:[Promise.resolve()]}); 31 | multilang.main({ 32 | input:'INPUT.md', 33 | langs:['xx'], 34 | output:'OUTPUT.md', 35 | directory:'aDirectory', 36 | silent:opts.silent, 37 | verbose:opts.verbose, 38 | chanout:opts.chanout, 39 | chanerr:opts.chanerr 40 | }).then(function(exitCode){ 41 | expect(readFileControl .calls).to.eql([['INPUT.md',{encoding: 'utf8'}]]); 42 | expect(changeDocControl.calls).to.eql([[contentOfDoc,'xx']]); 43 | expect(writeFileControl.calls).to.eql([['aDirectory'+path.sep+'OUTPUT.md','valid content']]); 44 | expect(exitCode).to.eql(0); 45 | done(); 46 | }).catch(function(err){ 47 | done(err); 48 | }).then(function(){ 49 | readFileControl .stopControl(); 50 | changeDocControl.stopControl(); 51 | writeFileControl.stopControl(); 52 | obtainLangsControl.stopControl(); 53 | }); 54 | } 55 | it('do simple task silently',function(done){ 56 | doSimpleSuccessTask(done, {silent:true}); 57 | }); 58 | it('do simple task with warnings',function(done){ 59 | var getWarningsControl=expectCalled.control(multilang,'getWarnings',{returns:[[{line: 1, text:'this is the warning\n with 2 lines'}]]}); 60 | var chanout = new MiniStreamCapture(); 61 | var chanerr = new MiniStreamCapture(); 62 | doSimpleSuccessTask(function(err){ 63 | if(err){ 64 | done(err); 65 | }else{ 66 | try{ 67 | expect(chanout.getContent()).to.eql( 68 | "Processing 'INPUT.md'...\n"+ 69 | "Generating 'xx', writing to 'aDirectory"+path.sep+"OUTPUT.md'...\n"+ 70 | "Generated 'xx', file 'aDirectory"+path.sep+"OUTPUT.md'.\n" 71 | ); 72 | expect(chanerr.getContent()).to.eql("line 1: this is the warning\n with 2 lines\n"); 73 | expect(getWarningsControl.calls).to.eql([[contentOfDoc]]); 74 | done(); 75 | }catch(err){ 76 | done(err); 77 | } 78 | } 79 | getWarningsControl.stopControl(); 80 | }, {silent:false, verbose:true, chanout:chanout, chanerr:chanerr}); 81 | }); 82 | it('fail on simple task',function(done){ 83 | var readFileControl =expectCalled.control(fs,'readFile',{returns:[Promise.resolve('content of INPUT')]}); 84 | var changeDocControl=expectCalled.control(multilang,'changeDoc',{returns:['valid content']}); 85 | var writeFileControl=expectCalled.control(fs,'writeFile',{returns:[Promise.reject(new Error("invalid name"))]}); 86 | multilang.main({ 87 | input:'INPUT.md', 88 | langs:['xx'], 89 | output:'OUTPUT.md', 90 | directory:'aDirectory', 91 | silent:true 92 | }).then(function(exitCode){ 93 | done("Must return a reject promise, because writeFile fails"); 94 | }).catch(function(err){ 95 | expect(err).to.be.a(Error); 96 | expect(err.message).to.match(/invalid name/); 97 | done(); 98 | }).then(function(){ 99 | readFileControl .stopControl(); 100 | changeDocControl.stopControl(); 101 | writeFileControl.stopControl(); 102 | }).catch(function(err){ 103 | done(err); 104 | }); 105 | }); 106 | var originalContent='The content of the doc\nwith \n comment'; 107 | var strippedContent='The content of the doc\nwith\n comment'; 108 | function testStripComments(done, opts, obtainedFile, expectedContent){ 109 | var obtainLangsControl=expectCalled.control(multilang,'obtainLangs',{returns:[ 110 | {main:'mm', langs:{xx:{fileName:obtainedFile}}}, 111 | {main:'mm', langs:{xx:{fileName:obtainedFile}}}, 112 | ]}); 113 | var readFileControl =expectCalled.control(fs,'readFile',{returns:[Promise.resolve(originalContent)]}); 114 | var changeDocControl=expectCalled.control(multilang,'changeDoc',{returns:[expectedContent]}); 115 | var writeFileControl=expectCalled.control(fs,'writeFile',{returns:[Promise.resolve()]}); 116 | multilang.main({ 117 | input:'INPUT.md', 118 | langs:['xx'], 119 | output:obtainedFile, 120 | directory:'aDirectory', 121 | silent:opts.silent, 122 | verbose:opts.verbose, 123 | chanout:opts.chanout, 124 | chanerr:opts.chanerr, 125 | stripComments:opts.stripComments 126 | }).then(function(exitCode){ 127 | expect(readFileControl .calls).to.eql([['INPUT.md',{encoding: 'utf8'}]]); 128 | expect(changeDocControl.calls).to.eql([[originalContent,'xx']]); 129 | expect(writeFileControl.calls).to.eql([['aDirectory'+path.sep+obtainedFile,expectedContent]]); 130 | expect(exitCode).to.eql(0); 131 | done(); 132 | }).catch(function(err){ 133 | done(err); 134 | }).then(function(){ 135 | readFileControl .stopControl(); 136 | changeDocControl.stopControl(); 137 | writeFileControl.stopControl(); 138 | obtainLangsControl.stopControl(); 139 | }); 140 | } 141 | it('strip-comments default with README.md',function(done){ 142 | testStripComments(done, {silent:true}, 'README.md', strippedContent); 143 | }); 144 | it('strip-comments default with other.md',function(done){ 145 | testStripComments(done, {silent:true}, 'normal.md', originalContent); 146 | }); 147 | it('strip-comments false with README.md',function(done){ 148 | testStripComments(done, {silent:true, stripComments:false}, 'README.md', originalContent); 149 | }); 150 | it('strip-comments false with other.md',function(done){ 151 | testStripComments(done, {silent:true, stripComments:false}, 'other.md', originalContent); 152 | }); 153 | it('strip-comments true with README.md',function(done){ 154 | testStripComments(done, {silent:true, stripComments:true}, 'README.md', strippedContent); 155 | }); 156 | it('strip-comments true with other.md',function(done){ 157 | testStripComments(done, {silent:true, stripComments:true}, 'other.md', strippedContent); 158 | }); 159 | it('fail on overlapped input/output',function(done){ 160 | var readFileControl =expectCalled.control(fs,'readFile',{returns:[Promise.resolve('content of INPUT')]}); 161 | var changeDocControl=expectCalled.control(multilang,'changeDoc',{returns:['valid content']}); 162 | var writeFileControl=expectCalled.control(fs,'writeFile',{returns:[Promise.resolve()]}); 163 | multilang.main({ 164 | input:'aDirectory/INPUT.md', 165 | langs:['xx'], 166 | output:'INPUT.md', 167 | directory:'aDirectory', 168 | silent:true 169 | }).then(function(exitCode){ 170 | done("Must fail because input and output are the same"); 171 | }).catch(function(err){ 172 | expect(err).to.be.a(Error); 173 | expect(err.message).to.match(/input and output should be different/); 174 | done(); 175 | }).then(function(){ 176 | readFileControl .stopControl(); 177 | changeDocControl.stopControl(); 178 | writeFileControl.stopControl(); 179 | }).catch(function(err){ 180 | done(err); 181 | }); 182 | }); 183 | }); 184 | 185 | -------------------------------------------------------------------------------- /LEEME.md: -------------------------------------------------------------------------------- 1 | 2 | # multilang 3 | 4 | 5 | Herramientas multilenguaje (primeramente para Markdown) 6 | 7 | 14 | 15 | 16 | [![npm-version](https://img.shields.io/npm/v/multilang.svg)](https://npmjs.org/package/multilang) 17 | [![downloads](https://img.shields.io/npm/dm/multilang.svg)](https://npmjs.org/package/multilang) 18 | [![build](https://img.shields.io/travis/codenautas/multilang/master.svg)](https://travis-ci.org/codenautas/multilang) 19 | [![coverage](https://img.shields.io/coveralls/codenautas/multilang/master.svg)](https://coveralls.io/r/codenautas/multilang) 20 | 21 | 22 | 23 | idioma: ![castellano](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png) 24 | también disponible en: 25 | [![inglés](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png)](README.md) - 26 | [![alemán](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-de.png)](LIESMICH.md) 27 | 28 | 29 | 30 | En un archivo tipo Markdown o html se escribe la documentación en varios idiomas. 31 | 32 | Uno de esos lenguajes es el principal, los otros están comentados con ![open](https://raw.githubusercontent.com/codenautas/multilang/master/img/comment-open.png) y ![close](https://raw.githubusercontent.com/codenautas/multilang/master/img/comment-close.png) 33 | 34 | Luego con `multilang` se extraen los otros lenguajes generando un archivo para cada uno de los otros lenguajes definidos 35 | 36 | 45 | 46 | ## Instalación 47 | 48 | 57 | 58 | ```sh 59 | $ npm install multilang -g 60 | ``` 61 | 62 | 63 | 64 | ## Uso 65 | 66 | 75 | 76 | ``` 77 | $ multilang doc-en.md 78 | ``` 79 | 80 | 81 | 82 | Genera los archivos especificados en la cabecera del archivo para los idiomas secundarios. 83 | 84 | 93 | 94 | ## Formato del documento multilenguaje 95 | 96 | Un documento multilenguaje es un documento HTML o Markdown escrito en un idioma principal, 97 | que contiene dentro del mismo documento la traducción a uno o varios idiomas secundarios. 98 | 99 | ### Ejemplo 100 | 101 | 119 | 120 | ``` 121 | 122 | 123 | 124 | language: ![English](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png) 125 | also available in: 126 | [Spanish](LEEME.md) [French](LISEZMOI.md) [German](LIESMICH.md) 127 | 128 | 129 | This is a little example 130 | 137 | 138 | 139 | "*" means all languages 140 | 147 | All you need is multilang! 148 | ``` 149 | 150 | 151 | 152 | El documento tiene en algún lugar un renglón con la directiva multilenguaje. Ejemplo: 153 | 154 | 163 | 164 | ``` 165 | 166 | ``` 167 | 168 | 169 | 170 | * *v0* es la versión del formato multilenguaje, 171 | * *en* es el lenguaje principal [ISO 639-1](http://es.wikipedia.org/wiki/ISO_639-1), en este caso inglés 172 | * *README.md* es el nombre del archivo principal, el que contiene el documento que se está procesando 173 | * *es* y *fr* son los lenguajes secundarios (español y francés) 174 | * *LEEME.md* es el nombre del documento en español 175 | * *LISEZMOID.md* es el nombre del documento en francés 176 | 177 | El siguiente renglón después de la directiva multilenguaje es la directiva que indica 178 | la presencia de los links a los otros documentos. Tiene el siguiente formato 179 | 180 | 189 | 190 | ``` 191 | 192 | ``` 193 | 194 | 195 | 196 | Lo siguientes renglones son los botones y links a los otros lenguajes. 197 | 198 | Las directivas terminan con un renglón en blanco. 199 | 200 | El resto del documento tiene el texto en los distintos idiomas, 201 | intercalando los idiomas en el orden en que están definidos en la directiva multilenguaje. 202 | 203 | Las secciones o subsecciones donde se cambia de idioma están señaladas con la directiva 204 | 205 | 214 | 215 | ``` 216 | [!--lang:fr--] 217 | ``` 218 | 219 | 220 | 221 | * *fr* en este ejemplo se indica que los renglones siguientes están escritos en francés 222 | * las secciones pueden empezar o terminar con [ o < y > o ] 223 | 224 | Cuando una parte del texto sea para todos los idiomas se puede poner un asterisco "*" en vez del código de idioma. 225 | 226 | Cuando empiece la sección del idioma principal en vez de un corchete "]" la directiva cierra con un signo de mayor ">"; 227 | así se cierra el comentario HTML. Cuando termina la sección del idioma principal el siguiente indicador de idioma comienza con 228 | un signo de menor "<" en vez de un corchete "[" para que empiece un nuevo cometario HTML 229 | y no se visualice el texto en los idiomas secundarios. 230 | 231 | 240 | 241 | ## API 242 | 243 | ```js 244 | 245 | var fs = require('fs'); 246 | var multilang = require('multilang'); 247 | 248 | var englishText = fs.readFileSync('README.md', {encoding:'utf8'}); 249 | 250 | var warnings = multilang.getWarnings(englishText); 251 | if(warnings.lengt){ 252 | console.log('WARN', warnings); 253 | } 254 | 255 | var spanishText = multilang.changeDoc(englishText,'es'); 256 | 257 | console.log('spanish.md',spanishText); 258 | ``` 259 | 260 | 261 | 262 | (nota acerca del ejemplo anterior: no use funciones ***Sync***rónicas en producción, 263 | use las versiones asincrónicas 264 | o basadas en [promesas](http://npmjs.com/package/fs-promise) 265 | como se ilustra en [codenautas](https://github.com/codenautas/codenautas/blob/master/examples/promises.md) 266 | 267 | función | uso 268 | ---------------------|------------------------------- 269 | changeDoc(text,lang) | dado un texto multilang y un código de lenguaje obtiene el texto correspondiente a ese lenguaje 270 | getWarnings(text) | obtiene una lista de advertencias a partir de un texto multilang 271 | 272 | 297 | 298 | ## License 299 | 300 | [MIT](LICENSE) 301 | 302 | ................................... 303 | -------------------------------------------------------------------------------- /bin/multilang.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // CMD-TOOL 4 | var multilang={}; 5 | 6 | var yaml = require('js-yaml'); 7 | var fs = require('fs-promise'); 8 | var stripBom = require('strip-bom-string'); 9 | var Path = require('path'); 10 | var changing = require('best-globals').changing; 11 | 12 | // locals 13 | // matches: m[1]: LB, m[2]: lang, m[3]: RB 14 | var reLangSec=/([<\[])!--lang:(.*)--([>\]])/; 15 | var reTrimWS = /([\t\r ]*)$/g; 16 | var imgUrl = 'https://raw.githubusercontent.com/codenautas/multilang/master/img/'; 17 | 18 | multilang.defLang='en'; 19 | multilang.stripCommentsFlag = null; 20 | 21 | multilang.langs={ 22 | en:{ 23 | name: 'English', 24 | abr: 'en', 25 | languages:{ 26 | en: 'English', 27 | es: 'Spanish', 28 | it: 'Italian', 29 | ru: 'Russian', 30 | de: 'German' 31 | }, 32 | phrases:{ 33 | language: 'language', 34 | 'also available in': 'also available in', 35 | 'DO NOT MODIFY DIRECTLY': 'DO NOT MODIFY DIRECTLY THIS FILE WAS GENERATED BY multilang.js' 36 | } 37 | } 38 | }; 39 | 40 | // esto se va a inicializar con los yaml de ./langs/lang-*.yaml 41 | multilang.changeDoc=function changeDoc(documentText,lang){ 42 | var obtainedLangs=this.obtainLangs(documentText); 43 | var langConv = this.parseLang(lang); 44 | var parts=this.splitDoc(documentText); 45 | var buttonSection=this.generateButtons(obtainedLangs,lang); // we just need the content 46 | return parts.map(function(part){ 47 | if('special' in part) { 48 | switch(part.special){ 49 | case 'header': 50 | return (part.withBom?'\ufeff':'')+ 51 | '\n'; 56 | case 'buttons': 57 | return buttonSection+'\n\n'; 58 | } 59 | } else { 60 | if(part.all || part.langs[lang]){ 61 | return part.text; 62 | } 63 | return ''; 64 | } 65 | }).join(''); 66 | }; 67 | 68 | multilang.obtainLangs=function obtainLangs(docHeader){ 69 | var all_langs = {}; 70 | var def_lang = null; 71 | var langs = /)/.exec(docHeader); 72 | if(langs) { 73 | var lang_re = /([a-z]{2}):([^.]+\.(md|html))/g; 74 | var lang; 75 | while(null !== (lang = lang_re.exec(langs[1]))) { 76 | if(null === def_lang) { def_lang = lang[1]; } 77 | all_langs[lang[1]] = {'fileName' : lang[2]}; 78 | } 79 | } 80 | return {main:def_lang, langs:all_langs}; 81 | }; 82 | 83 | multilang.generateButtons=function generateButtons(docHeader,lang) { 84 | if(! this.langs[lang]) { this.langs[lang] = this.parseLang(lang); } 85 | var ln = changing(this.langs[this.defLang], this.langs[lang]); 86 | var r=['\n\n']; 87 | r.push(ln.phrases.language+': !['+ln.name+']('+imgUrl+'lang-'+ln.abr+'.png)\n'); 88 | r.push(ln.phrases['also available in']+':'); 89 | var others=[]; 90 | /*jshint forin: false */ 91 | for(var lother in docHeader.langs) { 92 | if(lother === lang) { continue; } 93 | var lname = ln.languages[lother]; 94 | others.push('\n[!['+lname+']('+imgUrl+'lang-'+lother+'.png)]('+docHeader.langs[lother].fileName+')'); 95 | } 96 | /*jshint forin: true */ 97 | r.push(others.join(' -')); 98 | return r.join(''); 99 | }; 100 | 101 | multilang.splitDoc=function splitDoc(documentText){ 102 | var r = []; 103 | r.push({special:'header', withBom:'\uFEFF'===documentText.substring(0, 1)}); 104 | var doc = r[0].withBom ? documentText.substring(1) : documentText; 105 | var docLines = doc.split("\n"); 106 | var inButtons=false; 107 | var inTextual=false; 108 | var inLang=false; 109 | var inAll=false; 110 | var haveButtonsContent=false; 111 | for(var ln=0; ln)+)/); 116 | if(m){ 117 | if("buttons"===m[2]) { 118 | r.push({special:m[2]}); 119 | inButtons=true; 120 | inAll=false; 121 | continue; 122 | } 123 | else { continue; } 124 | } else { 125 | m = line.match(reLangSec); 126 | if(m) { 127 | inLang = true; 128 | inAll=false; 129 | if("*" !== m[2]) { 130 | var langs = m[2].split(","); 131 | var okLangs = {}; 132 | for(var l=0; l'}); 208 | } else { 209 | var obtainedLangsKeys = Object.keys(obtainedLangs.langs); 210 | var docLines = doc.split("\n"); 211 | var prevLang=null, curLang=null; 212 | var lastLang = obtainedLangsKeys[obtainedLangsKeys.length-1]; 213 | var inCode = false; 214 | var ln=0; 215 | var prevClosing=false; 216 | var lastLangLine=false; 217 | var isFirstSection=true; 218 | for( ; ln" || curLang===obtainedLangs.main) { 227 | if((!prevClosing && '<'!== m[1]) || (prevClosing !==']' && '['=== m[1])) { 228 | warns.push({line: ln+1, text: 'unbalanced start "'+m[1]+'"' }); 229 | } 230 | if(obtainedLangs.main === curLang && ">" !== m[3]) { 231 | warns.push({line: ln+1, text: 'main lang must end with ">" (lang:%)', params: [obtainedLangs.main]}); 232 | } 233 | } 234 | else if(prevClosing !== '') { 235 | if(prevClosing === "]" && m[1] !== "[") { 236 | warns.push({line: ln+1, text: 'unbalanced "["'}); 237 | } else if(prevClosing===">" && m[1] !== "<") { 238 | warns.push({line: ln+1, text: 'unbalanced "<"'}); 239 | } 240 | } 241 | if("*" === m[2]) { 242 | if(prevLang !== "*" && prevLang !== lastLang) { 243 | warns.push({line: ln+1, text: 'lang:* must be after other lang:* or after last lang section (%)', 244 | params: [lastLang] }); 245 | } 246 | if(">" !== m[3]) { 247 | warns.push({line: ln+1, text: 'lang:* must end with ">"'}); 248 | } 249 | } 250 | else if("*" !== curLang && -1 === obtainedLangsKeys.indexOf(curLang)) { 251 | warns.push({line: ln+1, text: '"lang:%" not included in the header', params: [curLang]}); 252 | } 253 | multilang.checkForMissingLangs(obtainedLangs.langs, prevLang, curLang, warns, ln+1, isFirstSection); 254 | isFirstSection=false; 255 | prevClosing = m[3]; 256 | prevLang = curLang; 257 | } else { // in language body 258 | // check for lang clause 259 | if(line.match(/--lang:(.*)--/)) { 260 | warns.push({line: ln+1, text: 'lang clause must not be included in text line'}); 261 | lastLangLine = ln+1; 262 | } 263 | } 264 | } 265 | } 266 | multilang.checkForMissingLangs(obtainedLangs.langs, prevLang, '*', warns, ln); 267 | if(prevLang !== "*" && prevLang !== lastLang) { 268 | warns.push({line: lastLangLine, text: 'last lang must be \"*\" or \"%"\"', params: [lastLang]}); 269 | } 270 | if(prevLang && prevClosing && prevClosing !== ">") { 271 | warns.push({line: lastLangLine, text: 'last lang directive could\'n finish in "'+prevClosing+'"'}); 272 | } 273 | } 274 | return warns; 275 | }; 276 | 277 | multilang.getWarningsButtons=function getWarningsButtons(doc){ 278 | var langs = multilang.obtainLangs(doc); 279 | var langFiles = Object.keys(langs.langs).map(function(lang) { return langs.langs[lang].fileName; }); 280 | var currentLangFile = 0; 281 | var mainLang = langs.main; 282 | var docLines = doc.split("\n"); 283 | var btnLines = []; 284 | var bl = 0; 285 | var warns=[]; 286 | var inButtonsSection=false; 287 | var inLang = false; 288 | var haveMultilangButtons=false; 289 | for(var ln=0; lnbl) { 310 | if(btnLines[bl] !== "" && docLine !== btnLines[bl]) { 311 | var warningBadLine = {line:ln+1, text:'button section does not match. Expected:\n'+btnLines[bl]+'\n'}; 312 | if(! docLine.match(/^(\[!\[)/)) { 313 | warns.push(warningBadLine); 314 | } else { 315 | var expectedFileName = langFiles[++currentLangFile]; 316 | var ref=/\(([^):]+)\)/.exec(docLine); 317 | if(ref) { 318 | warns.push({line:ln+1, text:"referenced document '"+ref[1]+"' does not exists in multilang header, expecting '"+expectedFileName+"'"}); 319 | } else { 320 | warns.push(warningBadLine); 321 | } 322 | } 323 | } 324 | ++bl; 325 | } 326 | } 327 | } 328 | if(! haveMultilangButtons) { 329 | warns.push({line:0, text:'missing section '}); 330 | } 331 | return warns; 332 | }; 333 | 334 | multilang.getWarnings=function getWarnings(doc){ 335 | return this.getWarningsButtons(doc).concat(this.getWarningsLangDirective(doc)); 336 | }; 337 | 338 | multilang.stringizeWarnings=function stringizeWarnings(warns) { 339 | var r=[]; 340 | for(var w=0; w])|([^-]+[^>]))+\s*)+-->)/g; 353 | var reS = //; 355 | var reT = /```/; 356 | var inComment = false; 357 | var inTicks = false; 358 | for(var ln=0; ln1 && parameters.output){ 432 | throw new Error('parameter output with more than one lang'); 433 | } 434 | if(langs.length<1){ 435 | throw new Error('no lang specified (or main lang specified)'); 436 | } 437 | if(!parameters.directory) { 438 | throw new Error('no output directory specified'); 439 | } 440 | if(!parameters.silent){ 441 | (parameters.chanerr || process.stderr).write(multilang.stringizeWarnings(multilang.getWarnings(readContent))); 442 | } 443 | // verificar que la salida no contenga a la entrada 444 | var outFiles = []; 445 | if(parameters.output) { 446 | outFiles.push({file:Path.normalize(parameters.directory + "/" + parameters.output),lang:langs[0]}); 447 | } else { 448 | langs.map(function(lang) { 449 | outFiles.push({file:Path.normalize(parameters.directory + "/" + obtainedLangs.langs[lang].fileName), lang:lang}); 450 | }); 451 | } 452 | var inputDir = Path.dirname(parameters.input); 453 | var inputFile = Path.resolve((inputDir !== '' ? inputDir : process.cwd())+"/"+Path.basename(parameters.input)); 454 | for(var f=0; f\n'+ 49 | '\n'+ 50 | 'langue: ![français](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-fr.png)\n'+ 51 | 'also available in:\n'+ 52 | '[![anglais](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png)](readme.md)' 53 | ); 54 | }); 55 | it('generate the button section from yamls', function(){ 56 | var docLangs={ 57 | main:'en', 58 | langs:{ 59 | en:{fileName:'multilanguage.md'}, 60 | es:{fileName:'multilenguaje.md'}, 61 | it:{fileName:'multilingua.md' }, 62 | ru:{fileName:'мультиязычный.md'}, 63 | de:{fileName:'mehrsprachig.md'} 64 | } 65 | }; 66 | var buttonSection = multilang.generateButtons(docLangs,'es'); 67 | expect(buttonSection).to.eql( 68 | '\n'+ 69 | '\n'+ 70 | 'idioma: ![castellano](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png)\n'+ 71 | 'también disponible en:\n'+ 72 | '[![inglés](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png)](multilanguage.md) -\n'+ 73 | '[![italiano](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-it.png)](multilingua.md) -\n'+ 74 | '[![ruso](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-ru.png)](мультиязычный.md) -\n'+ 75 | '[![alemán](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-de.png)](mehrsprachig.md)' 76 | ); 77 | }); 78 | it('separate the content of original doc', function(){ 79 | var doc='\ufeffFirst lines\n'+ 80 | ' \n'+ // ignored when split 81 | 'Other lines\n'+ 82 | '\n'+ 83 | 'Not ignored\n'+ 84 | ' \t \n'+ 85 | '\n'+ 86 | 'not blank line\n'+ 87 | 'other not blank line\n'+ 88 | ' \t \r\n'+ // one blank line separates de header, may by has or not spaces or \r before the \n 89 | 'if no prefix is for all languajes\n'+ 90 | '\n'+ // fake directive, is in textual section 101 | 'more textual text\n'+ 102 | '```\n'+ // end of textual section 103 | 'last line could not have endline marker'; 104 | doc += '\n\n'+ 135 | 'more textual text\n'+ 136 | '```\n'+ 137 | 'last line could not have endline marker\n' 138 | }, 139 | { 140 | langs:{ es:true }, 141 | text:'# Multilenguaje (Multilanguage)\n'+ 142 | '\n'+ 143 | 'Esto es una prueba de archivos markdown multilenguajes.\n'+ 144 | '\n'+ 145 | 'El principal objetivo es escribir la documentación en un único fuente.\n'+ 146 | '\n' 147 | } 148 | ]); 149 | }); 150 | it('parse yaml files', function(){ 151 | // quizás haya que borrar las llamadas a es e it y dejar solo ru (cuando haya que actualizar el caso) 152 | var parsedLang = multilang.parseLang('es'); 153 | expect(parsedLang).to.eql({ 154 | name: 'castellano', 155 | abr: 'es', 156 | languages: { 157 | en: 'inglés' , 158 | es: 'español' , 159 | it: 'italiano', 160 | ru: 'ruso', 161 | de: 'alemán', 162 | fr: 'francés', 163 | ja: 'japonés' 164 | }, 165 | phrases: { 166 | language: 'idioma', 167 | 'also available in': 'también disponible en', 168 | 'DO NOT MODIFY DIRECTLY': 'NO MODIFIQUE ESTE ARCHIVO. FUE GENERADO AUTOMÁTICAMENTE POR multilang.js' 169 | } 170 | }); 171 | parsedLang = multilang.parseLang('it'); 172 | expect(parsedLang).to.eql({ 173 | name: 'italiano', 174 | abr: 'it', 175 | languages: { 176 | en: 'inlgese', 177 | es: 'spagnolo', 178 | it: 'italiano', 179 | ru: 'russo', 180 | de: 'tedesco', 181 | fr: 'francese', 182 | ja: 'giapponese' 183 | }, 184 | phrases: { 185 | language: 'lingua', 186 | 'also available in': 'disponibile anche in', 187 | 'DO NOT MODIFY DIRECTLY': 'DO NOT MODIFY DIRECTLY THIS FILE WAS GENERATED BY multilang.js' 188 | } 189 | }); 190 | parsedLang = multilang.parseLang('ru'); 191 | expect(parsedLang).to.eql({ 192 | name: 'русский', 193 | abr: 'ru', 194 | languages: { 195 | en: 'английский', 196 | es: 'испанский', 197 | it: 'итальянский', 198 | ru: 'русский', 199 | de: 'по-неме́цки', 200 | fr: 'францу́зский', 201 | ja: 'Японский' 202 | }, 203 | phrases: { 204 | language: 'язык', 205 | 'also available in': 'также доступны в', 206 | 'DO NOT MODIFY DIRECTLY': 'DO NOT MODIFY DIRECTLY THIS FILE WAS GENERATED BY multilang.js' 207 | } 208 | }); 209 | }); 210 | }); 211 | describe('integration', function(){ 212 | it('generate the spanish file of the example (multilanguaje)', function(done){ 213 | fs.readFile('./examples/multilanguage.md',{encoding: 'utf8'}).then(function(englishDoc){ 214 | return fs.readFile('./examples/multilenguaje.md',{encoding: 'utf8'}).then(function(expectedSpanishDoc){ 215 | var obtainedSpanishDoc = multilang.changeDoc(englishDoc,'es'); 216 | expect(obtainedSpanishDoc).to.eql(expectedSpanishDoc); 217 | done(); 218 | }); 219 | }).catch(function(err){ 220 | done(err); 221 | }) 222 | }); 223 | it('generate the spanish file of the other example (with-spaces) (bug #9)', function(done){ 224 | fs.readFile('./examples/with-spaces.md',{encoding: 'utf8'}).then(function(englishDoc){ 225 | return fs.readFile('./examples/con-espacios.md',{encoding: 'utf8'}).then(function(expectedSpanishDoc){ 226 | var obtainedSpanishDoc = multilang.changeDoc(englishDoc,'es'); 227 | expect(obtainedSpanishDoc).to.eql(expectedSpanishDoc); 228 | done(); 229 | }); 230 | }).catch(function(err){ 231 | done(err); 232 | }) 233 | }); 234 | it('generate the english file from a spanish multilang file (bug #20)', function(done){ 235 | fs.readFile('./examples/desde-es.md',{encoding: 'utf8'}).then(function(englishDoc){ 236 | return fs.readFile('./examples/from-es.md',{encoding: 'utf8'}).then(function(expectedSpanishDoc){ 237 | var obtainedSpanishDoc = multilang.changeDoc(englishDoc,'en'); 238 | expect(obtainedSpanishDoc).to.eql(expectedSpanishDoc); 239 | done(); 240 | }); 241 | }).catch(function(err){ 242 | done(err); 243 | }) 244 | }); 245 | }); 246 | describe('controls', function(){ 247 | it('generate the french text of the fake', function(done){ 248 | multilang.langs.fr=frenchIncompleteExample; 249 | fs.readFile('./examples/multilanguage.md',{encoding: 'utf8'}).then(function(englishDoc){ 250 | englishDoc=stripBom(englishDoc); 251 | var splitDocControl = expectCalled.control(multilang,'splitDoc',{returns:[[ 252 | {special: 'header', withBom:false}, 253 | {langs: {en: true}, text:"in other lang don't put"}, 254 | {langs: {fr: true}, text:"top line in french\r\n\n"}, 255 | {special: 'buttons'}, 256 | {langs: {en: true, es:true}, text:"two langs without fr"}, 257 | {langs: {fr: true}, text:'second section in french\n\n\n'}, 258 | {langs: {en: true, fr: true}, text:'section in mixed lang\nfor various langs\n\n'}, 259 | {all:true, text:'section for all'} 260 | ]]}); 261 | var generateButtonsControl = expectCalled.control(multilang,'generateButtons',{returns:[ 262 | '<--button line-->\n\r\nactual lang:\n' 263 | ]}); 264 | var obtainLangsControl = expectCalled.control(multilang,'obtainLangs'); 265 | var obtainedDoc = multilang.changeDoc(englishDoc,'fr'); 266 | var fakeDoc='\n'+ 267 | "top line in french\r\n\n"+ 268 | '<--button line-->\n\r\nactual lang:\n\n\n'+ 269 | 'second section in french\n\n\n'+ 270 | 'section in mixed lang\nfor various langs\n\n'+ 271 | 'section for all'; 272 | expect(obtainedDoc).to.eql(fakeDoc); 273 | expect(obtainLangsControl.calls.length).to.eql(1); 274 | expect(obtainLangsControl.calls[0][0]).to.contain( 275 | '' 276 | ); 277 | expect(generateButtonsControl.calls).to.eql([ 278 | [{ 279 | main:'en', 280 | langs:{ 281 | en:{fileName:'multilanguage.md'}, 282 | es:{fileName:'multilenguaje.md'}, 283 | it:{fileName:'multilingua.md' }, 284 | ru:{fileName:'мультиязычный.md'}, 285 | de:{fileName:'mehrsprachig.md'} 286 | } 287 | },'fr'] 288 | ]); 289 | expect(splitDocControl.calls.length).to.eql(1); 290 | splitDocControl.stopControl(); 291 | generateButtonsControl.stopControl(); 292 | obtainLangsControl.stopControl(); 293 | done(); 294 | }).catch(function(err){ 295 | done(err); 296 | }) 297 | }); 298 | it('generate warnings controling --lang:xx-- directive',function(){ 299 | var doc='\n'+ 300 | '\r\n'+ 301 | '[!--lang:es--]\r\n'+ // line 3 302 | 'spanish text\n'+ 303 | '\n'+ 304 | '\t [!--lang:fr-->\n'+ // line 6 305 | '\n'+ 319 | ''; 320 | var warnings=multilang.getWarningsLangDirective(doc); 321 | expect(warnings).to.eql([ 322 | {line: 3, text:'unbalanced start "["'}, 323 | {line: 7, text:'lang:* must be after other lang:* or after last lang section (%)', params:['en']}, 324 | {line: 7, text:'lang:* must end with ">"'}, 325 | {line: 7, text:'missing section for lang %', params:['es']}, // there must be sections for all languages 326 | {line: 7, text:'missing section for lang %', params:['en']}, // there must be sections for all languages 327 | {line: 8, text:'main lang must end with ">" (lang:%)', params:['fr']}, 328 | {line: 10, text:'unbalanced "["'}, 329 | {line:11, text:'unbalanced "["'}, 330 | {line:12, text:'lang clause must not be included in text line'}, 331 | {line:19, text:'"lang:%" not included in the header', params:['ru']}, 332 | {line:21, text:'missing section for lang %', params:['es']}, 333 | {line:21, text:'missing section for lang %', params:['en']}, // at the end of the file 334 | {line:20, text:'last lang must be \"*\" or \"%"\"', params:['en']} 335 | ]); 336 | }); 337 | it('generate warnings controling buttons',function(){ 338 | var doc='\ufeff'+ 339 | '\r\n'+ // line 1 340 | 'any text does not mind\n'+ 341 | '\n'+ 342 | '\n'+ // line 4 343 | '\n'+ 344 | 'the buttons section\n'+ 345 | 'ends here\n'+ 346 | '\n'+ 347 | 'Text for all languages'; 348 | var control=expectCalled.control(multilang,'generateButtons',{returns:[ 349 | '\n'+ 350 | '\n'+ 351 | 'the buttons section\n' + 352 | 'ends here\n', // call #1 353 | '\n'+ 354 | '\n'+ 355 | 'other button section for wrong answer\n', // call #2 356 | 'the buttons section\n' // call #3: for incomplete 357 | ]}); 358 | var warnings=multilang.getWarningsButtons(doc); 359 | expect(warnings).to.eql([]); // ok, no warnings 360 | var warnings=multilang.getWarningsButtons(doc); 361 | expect(warnings).to.eql([{line:6, text:'button section does not match. Expected:\n'+'other button section for wrong answer\n'}]); 362 | var warnings=multilang.getWarningsButtons(doc); 363 | expect(warnings).to.eql([{line:4, text:'button section does not match. Expected:\n'+'the buttons section\n'}]); 364 | control.stopControl(); 365 | }); 366 | it('generate warnings controling buttons (original)',function(){ 367 | var doc='\ufeff'+ 368 | '\r\n'+ // line 1 369 | 'any text does not mind\n'+ 370 | '\n'+ 371 | '\n'+ // line 4 372 | '\n'+ 373 | 'the buttons section\n'+ 374 | 'ends here\n'+ 375 | '(nombre.md)\n'+ 376 | '(name.md)\n'+ 377 | 'Text for all languages'; 378 | var control=expectCalled.control(multilang,'generateButtons',{returns:[ 379 | '\n'+ 380 | '\n'+ 381 | 'the buttons section\n' + 382 | 'ends here\n', // call #1 383 | '\n'+ 384 | '\n'+ 385 | 'other button section for wrong answer\n', // call #2 386 | 'the buttons section\n' // call #3: for incomplete 387 | ]}); 388 | var warnings=multilang.getWarningsButtons(doc); 389 | expect(warnings).to.eql([]); // ok, no warnings 390 | var warnings=multilang.getWarningsButtons(doc); 391 | expect(warnings).to.eql([{line:6, text:'button section does not match. Expected:\n'+'other button section for wrong answer\n'}]); 392 | var warnings=multilang.getWarningsButtons(doc); 393 | expect(warnings).to.eql([{line:4, text:'button section does not match. Expected:\n'+'the buttons section\n'}]); 394 | control.stopControl(); 395 | }); 396 | it('generate warnings controling buttons position',function(){ 397 | var doc='\ufeff'+ 398 | '\r\n'+ // line 1 399 | '\n'+ 400 | 'one spanish text\n'+ 401 | 'other spanish text\n'+ 402 | 'even more\n'+ 403 | '\n'+ // line 6 404 | '\n'+ 405 | 'the buttons section\n'+ 406 | 'ends here\n'+ 407 | '\n'+ 408 | 'Text for all languages'; 409 | var control=expectCalled.control(multilang,'generateButtons',{returns:[ 410 | ]}); 411 | var warnings=multilang.getWarningsButtons(doc); 412 | expect(warnings).to.eql([{line:6, text:'button section must be in main language or in all languages'}]); 413 | expect(control.calls.length).to.eql(0); 414 | control.stopControl(); 415 | }); 416 | it('generate warnings by calling warning parts',function(){ 417 | var doc='some doc'; 418 | var getWarningsButtonsControl=expectCalled.control(multilang,'getWarningsButtons',{returns:[[ 419 | {line:3, text:'this', params:[1,2,3]}, 420 | {line:4, text:'other text'} 421 | ]]}); 422 | var getWarningsLangDirectiveControl=expectCalled.control(multilang,'getWarningsLangDirective',{returns:[[ 423 | {line:11, text:'one %', params:['es']}, 424 | {line:1, text:'two'} 425 | ]]}); 426 | var warnings=multilang.getWarnings(doc); 427 | warnings.sort(bestGlobals.compareForOrder([{column:'line'},{column:'text'}])); 428 | // warnings=_.sortByAll(warnings,_.keys(warnings[0]||{})); 429 | expect(warnings).to.eql([ 430 | {line:1, text:'two'}, 431 | {line:3, text:'this', params:[1,2,3]}, 432 | {line:4, text:'other text'}, 433 | {line:11, text:'one %', params:['es']} 434 | ]); 435 | getWarningsLangDirectiveControl.stopControl(); 436 | getWarningsButtonsControl.stopControl(); 437 | }); 438 | var headerEnEs3lines='\n'+ 439 | '\n'+ 440 | 'english text (also seen in spanish)\n'; 441 | it('generate warnings controling ] [ balanced',function(){ 442 | var doc=headerEnEs3lines + 443 | '\n'+ 446 | 'english text 2\n'+ 447 | '\n'+ 450 | ''; 451 | var warnings=multilang.getWarningsLangDirective(doc); 452 | expect(warnings).to.eql([]); 453 | }); 454 | it('generate warnings in first or last ] [',function(){ 455 | var doc=headerEnEs3lines + 456 | 'spanish text\n'+ 457 | '\t [!--lang:en-->\n'+ // line 5 458 | 'english text 2\n'+ 459 | '\n'+ 472 | 'english text 2\n'+ 473 | '\n'+ 474 | 'spanish text\n'+ 475 | '\n'+ // line 9 476 | 'more spanish text 2\n'+ 477 | ''; 478 | var warnings=multilang.getWarningsLangDirective(doc); 479 | expect(warnings).to.eql([ 480 | {line: 9, text:'missing section for lang %', params:['en']} 481 | ]); 482 | }); 483 | it('generate warnings controlling missing sections (#11)',function(){ 484 | var doc='\n'+ 485 | '\n'+ 486 | 'english text (also seen in spanish)\n' + 487 | 'spanish text\n'+ 488 | '\t\n'+ 489 | 'english text 2\n'+ 490 | '\n'+ 491 | 'spanish text\n'+ 492 | ''; 493 | var warnings=multilang.getWarnings(doc); 494 | expect(warnings).to.eql([ 495 | {line:0, text:'missing section '}, 496 | {line:0, text:'missing section \n"+ 502 | "# pru\n"+ 503 | "\n"+ 504 | "pru module\n"+ 505 | "\n"+ 509 | "\n"+ 510 | "\n"+ 511 | "![designing](https://img.shields.io/badge/stability-designing-red.svg)\n"+ 512 | "[![npm-version](https://img.shields.io/npm/v/pru.svg)](https://npmjs.org/package/pru)\n"+ 513 | "[![downloads](https://img.shields.io/npm/dm/pru.svg)](https://npmjs.org/package/pru)\n"+ 514 | "[![build](https://img.shields.io/travis/codenautas/pru/master.svg)](https://travis-ci.org/codenautas/pru)\n"+ 515 | "[![coverage](https://img.shields.io/coveralls/codenautas/pru/master.svg)](https://coveralls.io/r/codenautas/pru)\n"+ 516 | "[![dependencies](https://img.shields.io/david/codenautas/pru.svg)](https://david-dm.org/codenautas/pru)\n"+ 517 | "\n"+ 518 | "\n"+ 519 | "\n"+ 520 | "\n"+ 521 | "idioma: ![castellano](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png)\n"+ 522 | "también disponible en:\n"+ 523 | "[![inglés](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png)]({{esDOC}})\n"+ 524 | "\n"+ 525 | "\n"+ 526 | "# Instalación\n"+ 527 | "\n"+ 530 | "```sh\n"+ 531 | "$ npm install pru\n"+ 532 | "```\n"+ 533 | "\n"+ 534 | "\n"+ 535 | "## Licencia\n"+ 536 | "\n"+ 539 | "\n"+ 540 | "[MIT](LICENSE)\n"+ 541 | "\n"; 542 | it('no warnings',function(){ 543 | var doc = docTemp.replace('{{esDOC}}', 'README.md'); 544 | var warnings=multilang.getWarningsButtons(doc); 545 | expect(warnings).to.eql([]); 546 | }); 547 | it('bad english .md',function(){ 548 | var doc = docTemp.replace('{{esDOC}}', 'WRONG.md'); 549 | var warnings=multilang.getWarningsButtons(doc); 550 | expect(warnings).to.eql([{ 551 | line: 23, 552 | text: "referenced document 'WRONG.md' does not exists in multilang header, expecting 'README.md'" 553 | }]); 554 | }); 555 | }); 556 | describe('generate warnings incorrect button(s) definitions (#24)', function() { 557 | var docTemplate = "\n"+ 558 | "# pru\n"+ 559 | "\n"+ 560 | "pru module\n"+ 561 | "\n"+ 565 | "\n"+ 566 | "\n"+ 567 | "![designing](https://img.shields.io/badge/stability-designing-red.svg)\n"+ 568 | "[![npm-version](https://img.shields.io/npm/v/pru.svg)](https://npmjs.org/package/pru)\n"+ 569 | "[![downloads](https://img.shields.io/npm/dm/pru.svg)](https://npmjs.org/package/pru)\n"+ 570 | "[![build](https://img.shields.io/travis/codenautas/pru/master.svg)](https://travis-ci.org/codenautas/pru)\n"+ 571 | "[![coverage](https://img.shields.io/coveralls/codenautas/pru/master.svg)](https://coveralls.io/r/codenautas/pru)\n"+ 572 | "[![dependencies](https://img.shields.io/david/codenautas/pru.svg)](https://david-dm.org/codenautas/pru)\n"+ 573 | "\n"+ 574 | "\n"+ 575 | "\n"+ 576 | "\n"+ 577 | "idioma: ![castellano](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png)\n"+ 578 | "también disponible en:\n"+ 579 | "{{LINE1}} -\n"+ 580 | "{{LINE2}}\n"+ 581 | "\n"+ 582 | "\n"+ 583 | "# Instalación\n"+ 584 | "\n"+ 587 | "```sh\n"+ 588 | "$ npm install pru\n"+ 589 | "```\n"+ 590 | "\n"+ 591 | "\n"+ 592 | "## Licencia\n"+ 593 | "\n"+ 596 | "\n"+ 597 | "[MIT](LICENSE)\n"+ 598 | "\n"; 599 | function genLangLine(titulo, pngsuf, docu) { 600 | return "[!["+titulo+"](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-"+pngsuf+".png)]("+docu+")"; 601 | } 602 | function lineEN() { return genLangLine('inglés', 'en', 'README.md'); } 603 | function lineDE() { return genLangLine('alemán', 'de', 'LESEN.md'); } 604 | it('no warnings',function(){ 605 | var doc = docTemplate.replace('{{LINE1}}', lineEN()).replace('{{LINE2}}', lineDE()); 606 | var warnings=multilang.getWarningsButtons(doc); 607 | expect(warnings).to.eql([]); 608 | }); 609 | it('bad english',function(){ 610 | var doc = docTemplate.replace('{{LINE1}}', genLangLine('inglés','en', 'WRONG.md')).replace('{{LINE2}}', lineDE()); 611 | //fs.writeFileSync("bad_en.md", doc); 612 | var warnings=multilang.getWarningsButtons(doc); 613 | expect(warnings).to.eql([ 614 | {line:23, text:"referenced document 'WRONG.md' does not exists in multilang header, expecting 'README.md'"} 615 | ]); 616 | }); 617 | it('bad english and german',function(){ 618 | var doc = docTemplate.replace('{{LINE1}}', genLangLine('inglés','en', 'WRONG.md')).replace('{{LINE2}}', genLangLine('alemán','de', 'WORST.md')); 619 | var warnings=multilang.getWarningsButtons(doc); 620 | expect(warnings).to.eql([ 621 | {line:23, text:"referenced document 'WRONG.md' does not exists in multilang header, expecting 'README.md'"}, 622 | {line:24, text:"referenced document 'WORST.md' does not exists in multilang header, expecting 'LESEN.md'"} 623 | ]); 624 | }); 625 | it('bad language line',function(){ 626 | var doc = docTemplate.replace('{{LINE1}}', "[![inglés]https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png]README.md").replace('{{LINE2}}', lineDE()); 627 | var warnings=multilang.getWarningsButtons(doc); 628 | expect(warnings).to.eql([ 629 | {line:23, text:"button section does not match. Expected:\n[![inglés](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png)](README.md) -\n"} 630 | ]); 631 | }); 632 | }); 633 | }); 634 | describe('auxiliary functions', function(){ 635 | it('stringizeWarnings correct input', function(done){ 636 | var warns = [ 637 | {line: 2, text: 'text from line two'}, 638 | {line: 57, text: 'this is the text of line fifty seven and has parameters (%)', params:'en'}, 639 | {line: 4, text: 'this is the text of line four'} 640 | ]; 641 | var stringizedWarn = multilang.stringizeWarnings(warns); 642 | expect(stringizedWarn).to.eql("line 2: text from line two\n" + 643 | "line 57: this is the text of line fifty seven and has parameters (en)\n" + 644 | "line 4: this is the text of line four\n"); 645 | done(); 646 | }); 647 | it('stringizeWarnings with null input', function(done){ 648 | var stringizedWarn = multilang.stringizeWarnings([]); 649 | expect(stringizedWarn).to.eql(''); 650 | done(); 651 | }); 652 | function compareSCio(input, output) { 653 | var stripped = multilang.stripComments(input); 654 | expect(stripped).to.eql(output); 655 | } 656 | it('stripComments with identical input and output', function(){ 657 | compareSCio('',''); 658 | compareSCio('\n','\n'); 659 | compareSCio('algo\n','algo\n'); 660 | compareSCio('\nalgo','\nalgo'); 661 | compareSCio('algo\nmas','algo\nmas'); 662 | }); 663 | it('stripComments input with comments', function(){ 664 | compareSCio('linea con una sola linea','linea con una sola linea'); 665 | compareSCio('hola \nlinea comun','hola \nlinea comun'); 666 | compareSCio('hola \nlinea comun','hola \nlinea comun'); 667 | compareSCio('primera\n\nsegunda línea','primera\nsegunda línea'); 668 | compareSCio('primera\n\nsegunda línea','primera\nsegunda línea'); 669 | compareSCio('\nprimera línea\n2\n3','primera línea\n2\n3'); 670 | compareSCio('\nprimera línea\n2+3=5','primera línea\n2+3=5'); 671 | compareSCio('', ''); 672 | compareSCio('Comentario con varias', 'Comentario con varias'); 673 | }); 674 | it('stripComments input with backticks', function(){ 675 | compareSCio('linea con ticks `dentro` y una linea','linea con ticks `dentro` y una linea'); 676 | compareSCio('linea con ticks\n```js\nvar js=true;\n```sigue','linea con ticks\n```js\nvar js=true;\n```sigue'); 677 | compareSCio('linea con ticks\n```html```sigue', 678 | 'linea con ticks\n```html```sigue'); 679 | }); 680 | it.skip/*coming soon*/('stripComments input with backticks in one line', function(){ 681 | compareSCio('abre ``', 682 | 'abre ``'); 683 | }); 684 | }); 685 | }); 686 | --------------------------------------------------------------------------------