initial translation support
This commit is contained in:
parent
33db337a7a
commit
f8e10a1e09
7 changed files with 154 additions and 26 deletions
|
@ -2,12 +2,12 @@ class Contextmenu<x, y>{
|
|||
static currentmenu: HTMLElement | "";
|
||||
name: string;
|
||||
buttons: [
|
||||
string,
|
||||
(this: x, arg: y, e: MouseEvent) => void,
|
||||
string | null,
|
||||
(this: x, arg: y) => boolean,
|
||||
(this: x, arg: y) => boolean,
|
||||
string
|
||||
string|(()=>string),
|
||||
(this: x, arg: y, e: MouseEvent) => void,
|
||||
string | null,
|
||||
(this: x, arg: y) => boolean,
|
||||
(this: x, arg: y) => boolean,
|
||||
string
|
||||
][];
|
||||
div!: HTMLDivElement;
|
||||
static setup(){
|
||||
|
@ -27,7 +27,7 @@ class Contextmenu<x, y>{
|
|||
this.buttons = [];
|
||||
}
|
||||
addbutton(
|
||||
text: string,
|
||||
text: string|(()=>string),
|
||||
onclick: (this: x, arg: y, e: MouseEvent) => void,
|
||||
img: null | string = null,
|
||||
shown: (this: x, arg: y) => boolean = _=>true,
|
||||
|
@ -58,7 +58,11 @@ class Contextmenu<x, y>{
|
|||
const intext = document.createElement("button");
|
||||
intext.disabled = !thing[4].bind(addinfo).call(addinfo, other);
|
||||
intext.classList.add("contextbutton");
|
||||
intext.textContent = thing[0];
|
||||
if(thing[0] instanceof Function){
|
||||
intext.textContent = thing[0]();
|
||||
}else{
|
||||
intext.textContent = thing[0];
|
||||
}
|
||||
console.log(thing);
|
||||
if(thing[5] === "button" || thing[5] === "submenu"){
|
||||
intext.onclick = thing[1].bind(addinfo, other);
|
||||
|
|
96
src/webpage/i18n.ts
Normal file
96
src/webpage/i18n.ts
Normal file
|
@ -0,0 +1,96 @@
|
|||
type translation={
|
||||
[key:string]:string|{[key:string]:string}
|
||||
};
|
||||
let res:()=>unknown=()=>{};
|
||||
class I18n{
|
||||
static lang:string;
|
||||
static translations:{[key:string]:string}[]=[];
|
||||
static done=new Promise<void>((res2,_reject)=>{
|
||||
res=res2;
|
||||
});
|
||||
static async create(json:translation|string,lang:string){
|
||||
if(typeof json === "string"){
|
||||
json=await (await fetch(json)).json() as translation;
|
||||
}
|
||||
const translations:{[key:string]:string}[]=[];
|
||||
let translation=json[lang];
|
||||
if(!translation){
|
||||
translation=json[lang[0]+lang[1]];
|
||||
if(!translation){
|
||||
console.error(lang+" does not exist in the translations");
|
||||
translation=json["en"];
|
||||
lang="en";
|
||||
}
|
||||
}
|
||||
translations.push(await this.toTranslation(translation,lang));
|
||||
if(lang!=="en"){
|
||||
translations.push(await this.toTranslation(json["en"],"en"))
|
||||
}
|
||||
this.lang=lang;
|
||||
this.translations=translations;
|
||||
res();
|
||||
}
|
||||
static getTranslation(msg:string,...params:string[]):string{
|
||||
let str:string|undefined;
|
||||
for(const json of this.translations){
|
||||
str=json[msg];
|
||||
if(str){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(str){
|
||||
return this.fillInBlanks(str,params);
|
||||
}else{
|
||||
throw new Error(msg+" not found")
|
||||
}
|
||||
}
|
||||
static fillInBlanks(msg:string,params:string[]):string{
|
||||
//thanks to geotale for the regex
|
||||
msg=msg.replace(/{{(.+?)}}/g,
|
||||
(str, match:string) => {
|
||||
const [op,strsSplit]=this.fillInBlanks(match,params).split(":");
|
||||
const [first,...strs]=strsSplit.split("|");
|
||||
switch(op.toUpperCase()){
|
||||
case "PLURAL":{
|
||||
const numb=Number(first);
|
||||
if(numb===0){
|
||||
return strs[strs.length-1];
|
||||
}
|
||||
return strs[Math.min(strs.length-1,numb-1)];
|
||||
}
|
||||
case "GENDER":{
|
||||
if(first==="male"){
|
||||
return strs[0];
|
||||
}else if(first==="female"){
|
||||
return strs[1];
|
||||
}else if(first==="neutral"){
|
||||
if(strs[2]){
|
||||
return strs[2];
|
||||
}else{
|
||||
return strs[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
);
|
||||
msg=msg.replace(/\$\d+/g,(str, match:string) => {
|
||||
const number=Number(match);
|
||||
if(params[number-1]){
|
||||
return params[number-1];
|
||||
}else{
|
||||
return str;
|
||||
}
|
||||
});
|
||||
return msg;
|
||||
}
|
||||
private static async toTranslation(trans:string|{[key:string]:string},lang:string):Promise<{[key:string]:string}>{
|
||||
if(typeof trans==='string'){
|
||||
return this.toTranslation((await (await fetch(trans)).json() as translation)[lang],lang);
|
||||
}else{
|
||||
return trans;
|
||||
}
|
||||
}
|
||||
}
|
||||
export{I18n};
|
|
@ -12,14 +12,12 @@ import{ MarkDown }from"./markdown.js";
|
|||
import { Bot } from "./bot.js";
|
||||
import { Role } from "./role.js";
|
||||
import { VoiceFactory } from "./voice.js";
|
||||
import { I18n } from "./i18n.js";
|
||||
|
||||
const wsCodesRetry = new Set([4000, 4003, 4005, 4007, 4008, 4009]);
|
||||
|
||||
class Localuser{
|
||||
badges: Map<
|
||||
string,
|
||||
{ id: string; description: string; icon: string; link: string }
|
||||
> = new Map();
|
||||
badges: Map<string,{ id: string; description: string; icon: string; link: string }> = new Map();
|
||||
lastSequence: number | null = null;
|
||||
token!: string;
|
||||
userinfo!: Specialuser;
|
||||
|
@ -67,8 +65,10 @@ class Localuser{
|
|||
"Content-type": "application/json; charset=UTF-8",
|
||||
Authorization: this.userinfo.token,
|
||||
};
|
||||
I18n.create("/translations/en.json","en")
|
||||
}
|
||||
gottenReady(ready: readyjson): void{
|
||||
async gottenReady(ready: readyjson): Promise<void>{
|
||||
await I18n.done;
|
||||
this.initialized = true;
|
||||
this.ready = ready;
|
||||
this.guilds = [];
|
||||
|
@ -200,10 +200,10 @@ class Localuser{
|
|||
try{
|
||||
const temp = JSON.parse(build);
|
||||
build = "";
|
||||
await this.handleEvent(temp);
|
||||
if(temp.op === 0 && temp.t === "READY"){
|
||||
returny();
|
||||
}
|
||||
await this.handleEvent(temp);
|
||||
}catch{}
|
||||
}
|
||||
})();
|
||||
|
@ -231,9 +231,9 @@ class Localuser{
|
|||
if(
|
||||
!(
|
||||
array[len - 1] === 255 &&
|
||||
array[len - 2] === 255 &&
|
||||
array[len - 3] === 0 &&
|
||||
array[len - 4] === 0
|
||||
array[len - 2] === 255 &&
|
||||
array[len - 3] === 0 &&
|
||||
array[len - 4] === 0
|
||||
)
|
||||
){
|
||||
return;
|
||||
|
@ -244,10 +244,11 @@ class Localuser{
|
|||
}else{
|
||||
temp = JSON.parse(event.data);
|
||||
}
|
||||
|
||||
await this.handleEvent(temp as readyjson);
|
||||
if(temp.op === 0 && temp.t === "READY"){
|
||||
returny();
|
||||
}
|
||||
await this.handleEvent(temp as readyjson);
|
||||
}catch(e){
|
||||
console.error(e);
|
||||
}finally{
|
||||
|
@ -367,7 +368,7 @@ class Localuser{
|
|||
break;
|
||||
}
|
||||
case"READY":
|
||||
this.gottenReady(temp as readyjson);
|
||||
await this.gottenReady(temp as readyjson);
|
||||
break;
|
||||
case"MESSAGE_UPDATE": {
|
||||
temp.d.guild_id ??= "@me";
|
||||
|
|
|
@ -11,6 +11,7 @@ import{ SnowFlake }from"./snowflake.js";
|
|||
import{ memberjson, messagejson }from"./jsontypes.js";
|
||||
import{ Emoji }from"./emoji.js";
|
||||
import{ Dialog }from"./dialog.js";
|
||||
import { I18n } from "./i18n.js";
|
||||
|
||||
class Message extends SnowFlake{
|
||||
static contextmenu = new Contextmenu<Message, undefined>("message menu");
|
||||
|
@ -44,9 +45,7 @@ class Message extends SnowFlake{
|
|||
return this.weakdiv?.deref();
|
||||
}
|
||||
//*/
|
||||
div:
|
||||
| (HTMLDivElement & { pfpparent?: Message | undefined; txt?: HTMLElement })
|
||||
| undefined;
|
||||
div:(HTMLDivElement & { pfpparent?: Message | undefined; txt?: HTMLElement })| undefined;
|
||||
member: Member | undefined;
|
||||
reactions!: messagejson["reactions"];
|
||||
static setup(){
|
||||
|
@ -56,13 +55,13 @@ class Message extends SnowFlake{
|
|||
Message.setupcmenu();
|
||||
}
|
||||
static setupcmenu(){
|
||||
Message.contextmenu.addbutton("Copy raw text", function(this: Message){
|
||||
Message.contextmenu.addbutton(I18n.getTranslation.bind(I18n,"copyrawtext"), function(this: Message){
|
||||
navigator.clipboard.writeText(this.content.rawString);
|
||||
});
|
||||
Message.contextmenu.addbutton("Reply", function(this: Message){
|
||||
Message.contextmenu.addbutton(I18n.getTranslation.bind(I18n,"reply"), function(this: Message){
|
||||
this.channel.setReplying(this);
|
||||
});
|
||||
Message.contextmenu.addbutton("Copy message id", function(this: Message){
|
||||
Message.contextmenu.addbutton(I18n.getTranslation.bind(I18n,"copymessageid"), function(this: Message){
|
||||
navigator.clipboard.writeText(this.id);
|
||||
});
|
||||
Message.contextmenu.addsubmenu(
|
||||
|
|
|
@ -4,7 +4,7 @@ interface OptionsElement<x> {
|
|||
submit: () => void;
|
||||
readonly watchForChange: (func: (arg1: x) => void) => void;
|
||||
value: x;
|
||||
}
|
||||
}
|
||||
//future me stuff
|
||||
class Buttons implements OptionsElement<unknown>{
|
||||
readonly name: string;
|
||||
|
|
16
src/webpage/translations/en.json
Normal file
16
src/webpage/translations/en.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"MathMan05"
|
||||
],
|
||||
"last-updated": "2024/15/24",
|
||||
"locale": "en",
|
||||
"comment":"Don't know how often I'll update this top part lol"
|
||||
},
|
||||
"en": {
|
||||
"reply": "Reply",
|
||||
"copyrawtext":"Copy raw text",
|
||||
"copymessageid":"Copy message id"
|
||||
},
|
||||
"ru": "./ru.json"
|
||||
}
|
12
src/webpage/translations/ru.json
Normal file
12
src/webpage/translations/ru.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
],
|
||||
"last-updated": "2024/15/24",
|
||||
"locale": "ru",
|
||||
"comment":"I need some help with this :P"
|
||||
},
|
||||
"ru": {
|
||||
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue