Add low level i18n plugin

Manages languages that are available and is responsible for loading translated
content at the granularity of a plugin.

To be done:
 - APIs for store and retrieval
This commit is contained in:
Martin Hickey 2016-06-20 18:12:25 +01:00
parent 4b2ac10381
commit aca671f10b
13 changed files with 348 additions and 0 deletions

View file

@ -0,0 +1,2 @@
---
extends: "@elastic/kibana"

2
src/plugins/i18n/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
npm-debug.log*
node_modules

View file

@ -0,0 +1,25 @@
# i18n
> Core plugin which manages Kibana globalization
---
## development
See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment. Once you have completed that, use the following npm tasks.
<dl>
<dt><code>npm start</code></dt>
<dd>Start kibana and have it include this plugin</dd>
<dt><code>npm run build</code></dt>
<dd>Build a distributable archive</dd>
<dt><code>npm run test:browser</code></dt>
<dd>Run the browser tests in a real web browser</dd>
<dt><code>npm run test:server</code></dt>
<dd>Run the server tests using mocha</dd>
</dl>
For more information about any of these commands run `npm run ${task} -- --help`.

10
src/plugins/i18n/index.js Normal file
View file

@ -0,0 +1,10 @@
import route from './server/route';
export default function (kibana) {
return new kibana.Plugin({
init(server, options) {
// Add server routes and initalize the plugin here
route(server);
}
});
};

View file

@ -0,0 +1,23 @@
{
"name": "i18n",
"version": "0.0.0",
"description": "Core plugin which manages Kibana globalization",
"main": "index.js",
"scripts": {
"lint": "eslint",
"start": "plugin-helpers start",
"test:server": "plugin-helpers test_server",
"test:browser": "plugin-helpers test_browser",
"build": "plugin-helpers build",
"postinstall": "plugin-helpers postinstall"
},
"devDependencies": {
"@elastic/eslint-config-kibana": "0.0.2",
"@elastic/plugin-helpers": "5.0.0-beta1",
"babel-eslint": "4.1.8",
"bluebird": "^3.4.0",
"boom": "^3.2.1",
"eslint": "1.10.3",
"eslint-plugin-mocha": "1.1.0"
}
}

View file

@ -0,0 +1,123 @@
var fs = require('fs');
var path = require('path');
var process = require('child_process');
var os = require('os');
const TRANSLATION_FILE_EXTENSION = 'json';
module.exports = {
storePluginLanguageTranslations: function (pluginName, pluginTranslationPath, language) {
var translationFiles = [];
var languageList = [];
var translationStorePath = __dirname + '/data/store_translations';
var translationStorePluginPath = translationStorePath + '/' + pluginName;
var translationFileName = language + '.' + TRANSLATION_FILE_EXTENSION;
module.exports.getPluginTranslationDetails(pluginTranslationPath, translationFiles, languageList);
var langSupported = false;
for (var langIndx in languageList) {
if (language === languageList[langIndx]) {
langSupported = true;
break;
}
}
if (!langSupported) {
return false;
}
if (!fs.existsSync(translationStorePluginPath)) {
createDirectoriesRecursively(translationStorePluginPath);
}
for (var fileIndx in translationFiles) {
if (!translationFiles.hasOwnProperty(fileIndx)) continue;
var translationFile = translationFiles[fileIndx];
var pluginTranslationFileName = getFileName(translationFile);
if (pluginTranslationFileName !== translationFileName) continue;
var translationJson = require(translationFile);
var fileToWrite = translationStorePluginPath + '/' + translationFileName;
saveTranslationToFile(fileToWrite, translationJson);
}
return translationFiles;
},
getPluginLanguageTranslation: function (pluginName, language) {
var translationStorePath = __dirname + '/data/store_translations';
var translationStorePluginPath = translationStorePath + '/' + pluginName;
var translationFileName = language + '.' + TRANSLATION_FILE_EXTENSION;
var translationFile = translationStorePluginPath + '/' + translationFileName;
var translationStr = fs.readFileSync(translationFile);
return JSON.parse(translationStr);
},
getPluginTranslationDetails: function (pluginTranslationPath, translationFiles, languageList) {
getFilesRecursivelyFromTopDir(pluginTranslationPath, function parseDetails(fullPath) {
var files = getFilesFromDir(fullPath);
var fileLength = files.length;
for (var i = 0; i < fileLength; i++) {
var fullFilePath = files[i];
var fileName = getFileName(fullFilePath);
var fileExt = fileName.split('.').pop();
if (fileName === fileExt) continue;
if (fileExt !== TRANSLATION_FILE_EXTENSION) continue;
translationFiles.push(fullFilePath);
var lang = fileName.substr(0, fileName.lastIndexOf('.'));
if (languageList.indexOf(lang) !== -1) {
continue;
}
languageList.push(lang);
}
});
}
};
function saveTranslationToFile(translationFullFileName, translationJson) {
var jsonToWrite = [];
if (fs.existsSync(translationFullFileName)) {
var prevTranslationJson = require(translationFullFileName);
jsonToWrite = prevTranslationJson.concat(translationJson);
} else {
jsonToWrite = translationJson;
}
fs.writeFileSync(translationFullFileName, JSON.stringify(jsonToWrite, null, 4));
}
function getFilesRecursivelyFromTopDir(topDir, fileCallback) {
fs.readdirSync(topDir).forEach(function (name) {
var fullPath = path.join(topDir, name);
var stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
fileCallback(fullPath);
}
});
}
function getFilesFromDir(dir) {
var fileList = [];
var files = fs.readdirSync(dir);
for (var i in files) {
if (!files.hasOwnProperty(i)) continue;
var name = dir + '/' + files[i];
if (!fs.statSync(name).isDirectory()) {
fileList.push(name);
}
}
return fileList;
}
function getFileName(fullPath) {
return fullPath.replace(/^.*[\\\/]/, '');
}
// Added this function because 'mkdirp' does not add more than 2 subdirectories
function createDirectoriesRecursively(fullDir) {
process.exec('mkdir -p ' + fullDir, function (err,stdout,stderr) {
if (err) throw err;
});
}

View file

@ -0,0 +1,12 @@
export default function (server) {
server.route({
path: '/api/i18n/default',
method: 'GET',
handler(req, reply) {
reply('Hello World!');
}
});
};

View file

@ -0,0 +1,6 @@
[
{
"NO_SSL": "Dont run the DE dev server using HTTPS",
"DEV": "Run the DE server with development mode defaults"
}
]

View file

@ -0,0 +1,9 @@
[{
"NO_SSL": "Dont run the dev server using HTTPS",
"DEV": "Run the server with development mode defaults"
},
{
"NO_RUN_SERVER": "Dont run the dev server",
"HOME": "Run along home now!"
}
]

View file

@ -0,0 +1,118 @@
import expect from 'expect.js';
var process = require('child_process');
var i18n = require('../i18n');
describe('Test plugin translations details for test_plugin_1', function () {
var pluginName = 'test_plugin_1';
var pluginTranslationPath = __dirname + '/' + pluginName + '/translations';
it('2 translation languages exist', function () {
var result = true;
var expectedLanguages = ['en', 'de'];
var actualLanguages = getPluginTranslationLanguages(pluginName, pluginTranslationPath);
if (actualLanguages.length !== expectedLanguages.length) {
result = false;
} else {
var index = actualLanguages.length;
actualLanguages.sort();
expectedLanguages.sort();
while (index--) {
if (actualLanguages[index] !== expectedLanguages[index]) {
result = false;
break;
}
}
}
expect(result).to.be(true);
});
it('2 translation languages exist and wrongly expecting 1', function () {
var result = true;
var expectedLanguages = ['de'];
var actualLanguages = getPluginTranslationLanguages(pluginName, pluginTranslationPath);
if (actualLanguages.length !== expectedLanguages.length) {
result = false;
} else {
var index = actualLanguages.length;
actualLanguages.sort();
expectedLanguages.sort();
while (index--) {
if (actualLanguages[index] !== expectedLanguages[index]) {
result = false;
break;
}
}
}
expect(result).to.be(false);
});
it('Translation files exist', function () {
var result = true;
var expectedFiles = [
pluginTranslationPath + '/view1/de.json',
pluginTranslationPath + '/view1/en.json',
pluginTranslationPath + '/view2/en.json'
];
var actualFiles = getPluginTranslationFiles(pluginName, pluginTranslationPath);
if (actualFiles.length !== expectedFiles.length) {
result = false;
} else {
var index = actualFiles.length;
actualFiles.sort();
expectedFiles.sort();
while (index--) {
if (actualFiles[index] !== expectedFiles[index]) {
result = false;
break;
}
}
}
expect(result).to.be(true);
});
});
describe('Test storing translations for test_plugin_1', function () {
var pluginName = 'test_plugin_1';
var pluginTranslationPath = __dirname + '/' + pluginName + '/translations';
it('Translation plugin bundle for English' , function () {
var result = true;
var language = 'en';
if (!i18n.storePluginLanguageTranslations(pluginName, pluginTranslationPath, language)) {
result = false;
} else {
var expectedTranslationJsonFile = __dirname + '/data/reference/' + pluginName + '/' + language + '.json';
var expectedTranslationJson = require(expectedTranslationJsonFile);
expectedTranslationJson = JSON.stringify(expectedTranslationJson);
var actualTranslationJson = i18n.getPluginLanguageTranslation(pluginName, language);
actualTranslationJson = JSON.stringify(actualTranslationJson);
if (actualTranslationJson !== expectedTranslationJson) {
result = false;
}
}
expect(result).to.be(true);
});
afterEach(function (done) {
var translationStorePath = __dirname + '/../data/store_translations';
process.exec('rm -rf ' + translationStorePath + '/test_plugin_1', function (err,stdout,stderr) {
if (err) throw err;
done();
});
});
});
function getPluginTranslationLanguages(pluginName, pluginTranslationPath) {
var translationFiles = [];
var languageList = [];
i18n.getPluginTranslationDetails(pluginTranslationPath, translationFiles, languageList);
return languageList;
}
function getPluginTranslationFiles(pluginName, pluginTranslationPath) {
var translationFiles = [];
var languageList = [];
i18n.getPluginTranslationDetails(pluginTranslationPath, translationFiles, languageList);
return translationFiles;
}

View file

@ -0,0 +1,6 @@
[
{
"NO_SSL": "Dont run the DE dev server using HTTPS",
"DEV": "Run the DE server with development mode defaults"
]
}

View file

@ -0,0 +1,6 @@
[
{
"NO_SSL": "Dont run the dev server using HTTPS",
"DEV": "Run the server with development mode defaults"
}
]

View file

@ -0,0 +1,6 @@
[
{
"NO_RUN_SERVER": "Dont run the dev server",
"HOME": "Run along home now!"
}
]