logic
This commit is contained in:
parent
8f97d6326c
commit
1d7bf5a08b
126
app/cache.js
Normal file
126
app/cache.js
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
|
||||||
|
export /*abstract */class Cache {
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @return {boolean}
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
/*protected */has(key) {
|
||||||
|
throw (new Error('not implemented'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @return {any}
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
/*protected */fetch(key) {
|
||||||
|
throw (new Error('not implemented'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @param {any} value
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
/*protected */store(key, value) {
|
||||||
|
throw (new Error('not implemented'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @param {()=>Promise<any>} retrieve
|
||||||
|
* @return {Promise<any>}
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
/*public */async get(key, retrieve) {
|
||||||
|
if (this.has(key)) {
|
||||||
|
const value = this.fetch(key);
|
||||||
|
return Promise.resolve(value);
|
||||||
|
} else {
|
||||||
|
const value = await retrieve();
|
||||||
|
this.store(key, value);
|
||||||
|
return Promise.resolve(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
class CacheNone extends Cache {
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
/*public */constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
/*protected */has(key) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
/*protected */fetch(key) {
|
||||||
|
throw (new Error('not possible'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
/*protected */store(key, value) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
export class CacheLocalstorage extends Cache {
|
||||||
|
/**
|
||||||
|
* @param {string} [corner] for separating the cache instance from others
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
/*public */constructor(corner = null) {
|
||||||
|
super();
|
||||||
|
this.corner = corner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
/*private */augmentKey(key) {
|
||||||
|
return ((this.corner === null) ? key : (this.corner + '/' + key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
/*protected */has(key) {
|
||||||
|
return (window.localStorage.getItem(this.augmentKey(key)) !== null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
/*protected */fetch(key) {
|
||||||
|
const valueRaw = window.localStorage.getItem(this.augmentKey(key));
|
||||||
|
const value = JSON.parse(valueRaw);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
/*protected */store(key, value) {
|
||||||
|
const valueRaw = JSON.stringify(value);
|
||||||
|
window.localStorage.setItem(this.augmentKey(key), valueRaw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
40
app/file.js
Normal file
40
app/file.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path
|
||||||
|
* @return Promise<string>
|
||||||
|
* @todo use Util.fetch instead?
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
export async function read (path) {
|
||||||
|
return (
|
||||||
|
new Promise(
|
||||||
|
(resolve, reject) => {
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
request.open('GET', '/' + path, true);
|
||||||
|
request.onreadystatechange = () => {
|
||||||
|
switch (request.readyState) {
|
||||||
|
case XMLHttpRequest.DONE: {
|
||||||
|
switch (request.status) {
|
||||||
|
case 0: {
|
||||||
|
reject(new Error('XMLHttpRequest failed'));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
resolve(request.responseText);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
console.warn('unhandled readyState "' + request.readyState + '"');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
request.send(null);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
39
app/index.js
39
app/index.js
|
@ -11,6 +11,7 @@ import _dompurify from 'dompurify'
|
||||||
import keyboardjs from 'keyboardjs'
|
import keyboardjs from 'keyboardjs'
|
||||||
|
|
||||||
import { ContinuousVoiceHandler, PushToTalkVoiceHandler, VADVoiceHandler, initVoice } from './voice'
|
import { ContinuousVoiceHandler, PushToTalkVoiceHandler, VADVoiceHandler, initVoice } from './voice'
|
||||||
|
import {initialize as localizationInitialize, translate} from './loc';
|
||||||
|
|
||||||
const dompurify = _dompurify(window)
|
const dompurify = _dompurify(window)
|
||||||
|
|
||||||
|
@ -1013,18 +1014,28 @@ function userToState () {
|
||||||
var voiceHandler
|
var voiceHandler
|
||||||
var testVoiceHandler
|
var testVoiceHandler
|
||||||
|
|
||||||
initVoice(data => {
|
function translateEverything() {
|
||||||
if (testVoiceHandler) {
|
}
|
||||||
testVoiceHandler.write(data)
|
|
||||||
}
|
async function main() {
|
||||||
if (!ui.client) {
|
await localizationInitialize(navigator.language);
|
||||||
if (voiceHandler) {
|
translateEverything();
|
||||||
voiceHandler.end()
|
initVoice(data => {
|
||||||
|
if (testVoiceHandler) {
|
||||||
|
testVoiceHandler.write(data)
|
||||||
}
|
}
|
||||||
voiceHandler = null
|
if (!ui.client) {
|
||||||
} else if (voiceHandler) {
|
if (voiceHandler) {
|
||||||
voiceHandler.write(data)
|
voiceHandler.end()
|
||||||
}
|
}
|
||||||
}, err => {
|
voiceHandler = null
|
||||||
log('Cannot initialize user media. Microphone will not work:', err)
|
} else if (voiceHandler) {
|
||||||
})
|
voiceHandler.write(data)
|
||||||
|
}
|
||||||
|
}, err => {
|
||||||
|
log('Cannot initialize user media. Microphone will not work:', err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
|
||||||
|
|
121
app/loc.js
Normal file
121
app/loc.js
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
import {CacheLocalstorage} from './cache';
|
||||||
|
import {read as fileRead} from './file';
|
||||||
|
// import {Util} from 'util';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the relative path to the directory containing the JSON localization files
|
||||||
|
*
|
||||||
|
* @var {string}
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
var _directory = 'loc';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the default language to use
|
||||||
|
*
|
||||||
|
* @var {string}
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
var _languageDefault = null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the fallback language to use
|
||||||
|
*
|
||||||
|
* @var {string}
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
var _languageFallback = null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var {Cache}
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
var _cache = null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* two level map with ISO-639-1 code as first key and translation id as second key
|
||||||
|
*
|
||||||
|
* @var {Map<string,Map<string,string>>}
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
var _data = {};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} language
|
||||||
|
* @return Promise<Map<string,string>>
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
async function retrieveData (language) {
|
||||||
|
const regexp = (new RegExp("^([a-z]{2})$"));
|
||||||
|
if (regexp.exec(language) === null) {
|
||||||
|
return Promise.reject(new Error('invalid language code "' + language + '"'));
|
||||||
|
} else {
|
||||||
|
const path = (_directory + '/' + language + '.json');
|
||||||
|
let content;
|
||||||
|
try {
|
||||||
|
content = await fileRead(path);
|
||||||
|
} catch (exception) {
|
||||||
|
return Promise.reject(new Error('could not load localization data for language "' + language + '": ' + error.toString()));
|
||||||
|
}
|
||||||
|
let data;
|
||||||
|
try {
|
||||||
|
data = JSON.parse(content);
|
||||||
|
} catch (exception) {
|
||||||
|
return Promise.reject(new Error('invalid JSON localization data for language "' + language + '": ' + exception.toString()));
|
||||||
|
}
|
||||||
|
return Promise.resolve(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} languageDefault
|
||||||
|
* @param {string} [languageFallback]
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
export async function initialize (languageDefault, languageFallback = 'en') {
|
||||||
|
_cache = new CacheLocalstorage('loc');
|
||||||
|
_languageFallback = languageFallback;
|
||||||
|
_languageDefault = languageDefault;
|
||||||
|
for (const language of [_languageFallback, _languageDefault]) {
|
||||||
|
if (_data.hasOwnProperty(language)) continue;
|
||||||
|
console.log('--', 'loading localization data for language "' + language + '" ...');
|
||||||
|
let data;
|
||||||
|
try {
|
||||||
|
data = await _cache.get(language, () => retrieveData(language));
|
||||||
|
} catch (exception) {
|
||||||
|
console.warn(exception.toString());
|
||||||
|
}
|
||||||
|
_data[language] = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets a translation by its key for a specific language
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
* @param {string} [languageChosen]
|
||||||
|
* @return {string}
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
export function translate (key, languageChosen = _languageDefault) {
|
||||||
|
let result = undefined;
|
||||||
|
for (const language of [languageChosen, _languageFallback]) {
|
||||||
|
if (_data.hasOwnProperty(language) && (_data[language] !== undefined) && _data[language].hasOwnProperty(key)) {
|
||||||
|
result = _data[language][key];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result === undefined) {
|
||||||
|
result = ('{{' + key + '}}');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
8
tools/build
Executable file
8
tools/build
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
## loc
|
||||||
|
mkdir -p dist/loc
|
||||||
|
cp -ruv loc/* dist/loc/
|
||||||
|
|
Loading…
Reference in a new issue