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 { ContinuousVoiceHandler, PushToTalkVoiceHandler, VADVoiceHandler, initVoice } from './voice'
|
||||
import {initialize as localizationInitialize, translate} from './loc';
|
||||
|
||||
const dompurify = _dompurify(window)
|
||||
|
||||
|
@ -1013,18 +1014,28 @@ function userToState () {
|
|||
var voiceHandler
|
||||
var testVoiceHandler
|
||||
|
||||
initVoice(data => {
|
||||
if (testVoiceHandler) {
|
||||
testVoiceHandler.write(data)
|
||||
}
|
||||
if (!ui.client) {
|
||||
if (voiceHandler) {
|
||||
voiceHandler.end()
|
||||
function translateEverything() {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await localizationInitialize(navigator.language);
|
||||
translateEverything();
|
||||
initVoice(data => {
|
||||
if (testVoiceHandler) {
|
||||
testVoiceHandler.write(data)
|
||||
}
|
||||
voiceHandler = null
|
||||
} else if (voiceHandler) {
|
||||
voiceHandler.write(data)
|
||||
}
|
||||
}, err => {
|
||||
log('Cannot initialize user media. Microphone will not work:', err)
|
||||
})
|
||||
if (!ui.client) {
|
||||
if (voiceHandler) {
|
||||
voiceHandler.end()
|
||||
}
|
||||
voiceHandler = null
|
||||
} 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