From 3a5a43d08e9f4efc42cbd6feff17401bd861d988 Mon Sep 17 00:00:00 2001 From: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com> Date: Wed, 5 Feb 2025 07:18:39 +0100 Subject: [PATCH] feat(webui): translations support Closes #900 --- static/webui/index.html | 207 ++++++++++++------------- static/webui/libs/i18next.min.js | 1 + static/webui/script.js | 242 +++++++++++++++++++++--------- static/webui/style.css | 4 +- static/webui/translations/en.json | 129 ++++++++++++++++ static/webui/translations/ru.json | 129 ++++++++++++++++ 6 files changed, 527 insertions(+), 185 deletions(-) create mode 100644 static/webui/libs/i18next.min.js create mode 100644 static/webui/translations/en.json create mode 100644 static/webui/translations/ru.json diff --git a/static/webui/index.html b/static/webui/index.html index ec0bee15..0db3f369 100644 --- a/static/webui/index.html +++ b/static/webui/index.html @@ -34,13 +34,13 @@
  • แบบไทย
  • - @@ -56,16 +56,16 @@ @@ -73,38 +73,35 @@
    -

    Login using your OpenWF account credentials.

    +

    - +
    - +
    - +
    -

    - Note: Changes made here will only be reflected in-game when the game re-downloads your - inventory. Visiting the navigation should be the easiest way to trigger that. -

    +

    -
    Add Items
    +
    - +
    -
    Warframes
    +
    - +
    @@ -114,11 +111,11 @@
    -
    Primary Weapons
    +
    - +
    @@ -130,11 +127,11 @@
    -
    Secondary Weapons
    +
    - +
    @@ -144,11 +141,11 @@
    -
    Melee Weapons
    +
    - +
    @@ -160,11 +157,11 @@
    -
    Archwing
    +
    - +
    @@ -174,11 +171,11 @@
    -
    Archwing Primary Weapons
    +
    - +
    @@ -190,11 +187,11 @@
    -
    Archwing Melee Weapons
    +
    - +
    @@ -204,11 +201,11 @@
    -
    Necramechs
    +
    - +
    @@ -220,11 +217,11 @@
    -
    Sentinels
    +
    - +
    @@ -234,11 +231,11 @@
    -
    Sentinel Weapons
    +
    - +
    @@ -250,7 +247,7 @@
    -
    Amps
    +
    @@ -260,7 +257,7 @@
    -
    K-Drives
    +
    @@ -270,23 +267,23 @@
    -
    Bulk Actions
    +
    - - - - - - + + + + + +
    - - - - - - + + + + + +
    @@ -295,14 +292,14 @@

    -
    Archon Shard Slots
    +
    -

    You can use these unlimited slots to apply a wide range of upgrades.

    +

    x - +
    @@ -311,14 +308,11 @@
    -

    - Note: Changes made here will only be reflected in-game when the game re-downloads your - inventory. Visiting the navigation should be the easiest way to trigger that. -

    +

    -
    Add Riven
    +
    - - - Need help with the fingerprint? + + +
    -
    Rivens
    +
    @@ -345,12 +339,12 @@
    -
    Mods
    +
    - +
    @@ -358,9 +352,9 @@
    -
    Bulk Actions
    +
    - +
    @@ -373,127 +367,113 @@
    Server
    -

    You must be an administrator to use this feature. To become an administrator, add "" to administratorNames in the config.json.

    +

    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    - +
    -
    Account
    +
    -

    - +

    +
    -

    You can provide a full or partial inventory response (client respresentation) here. All fields that are supported by the importer will be overwritten in your account.

    +

    - +
    @@ -510,8 +490,8 @@ - - + + @@ -520,5 +500,6 @@ + diff --git a/static/webui/libs/i18next.min.js b/static/webui/libs/i18next.min.js new file mode 100644 index 00000000..95e3f897 --- /dev/null +++ b/static/webui/libs/i18next.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).i18next=t()}(this,(function(){"use strict";const e=e=>"string"==typeof e,t=()=>{let e,t;const s=new Promise(((s,i)=>{e=s,t=i}));return s.resolve=e,s.reject=t,s},s=e=>null==e?"":""+e,i=/###/g,o=e=>e&&e.indexOf("###")>-1?e.replace(i,"."):e,n=t=>!t||e(t),r=(t,s,i)=>{const r=e(s)?s.split("."):s;let a=0;for(;a{const{obj:i,k:o}=r(e,t,Object);if(void 0!==i||1===t.length)return void(i[o]=s);let n=t[t.length-1],a=t.slice(0,t.length-1),l=r(e,a,Object);for(;void 0===l.obj&&a.length;)n=`${a[a.length-1]}.${n}`,a=a.slice(0,a.length-1),l=r(e,a,Object),l?.obj&&void 0!==l.obj[`${l.k}.${n}`]&&(l.obj=void 0);l.obj[`${l.k}.${n}`]=s},l=(e,t)=>{const{obj:s,k:i}=r(e,t);if(s&&Object.prototype.hasOwnProperty.call(s,i))return s[i]},h=(t,s,i)=>{for(const o in s)"__proto__"!==o&&"constructor"!==o&&(o in t?e(t[o])||t[o]instanceof String||e(s[o])||s[o]instanceof String?i&&(t[o]=s[o]):h(t[o],s[o],i):t[o]=s[o]);return t},u=e=>e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&");var c={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};const p=t=>e(t)?t.replace(/[&<>"'\/]/g,(e=>c[e])):t;const g=[" ",",","?","!",";"],d=new class{constructor(e){this.capacity=e,this.regExpMap=new Map,this.regExpQueue=[]}getRegExp(e){const t=this.regExpMap.get(e);if(void 0!==t)return t;const s=new RegExp(e);return this.regExpQueue.length===this.capacity&&this.regExpMap.delete(this.regExpQueue.shift()),this.regExpMap.set(e,s),this.regExpQueue.push(e),s}}(20),f=function(e,t){let s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:".";if(!e)return;if(e[t]){if(!Object.prototype.hasOwnProperty.call(e,t))return;return e[t]}const i=t.split(s);let o=e;for(let e=0;e-1&&re?.replace("_","-"),y={type:"logger",log(e){this.output("log",e)},warn(e){this.output("warn",e)},error(e){this.output("error",e)},output(e,t){console?.[e]?.apply?.(console,t)}};class v{constructor(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.init(e,t)}init(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.prefix=t.prefix||"i18next:",this.logger=e||y,this.options=t,this.debug=t.debug}log(){for(var e=arguments.length,t=new Array(e),s=0;s{this.observers[e]||(this.observers[e]=new Map);const s=this.observers[e].get(t)||0;this.observers[e].set(t,s+1)})),this}off(e,t){this.observers[e]&&(t?this.observers[e].delete(t):delete this.observers[e])}emit(e){for(var t=arguments.length,s=new Array(t>1?t-1:0),i=1;i{let[t,i]=e;for(let e=0;e{let[i,o]=t;for(let t=0;t1&&void 0!==arguments[1]?arguments[1]:{ns:["translation"],defaultNS:"translation"};super(),this.data=e||{},this.options=t,void 0===this.options.keySeparator&&(this.options.keySeparator="."),void 0===this.options.ignoreJSONStructure&&(this.options.ignoreJSONStructure=!0)}addNamespaces(e){this.options.ns.indexOf(e)<0&&this.options.ns.push(e)}removeNamespaces(e){const t=this.options.ns.indexOf(e);t>-1&&this.options.ns.splice(t,1)}getResource(t,s,i){let o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};const n=void 0!==o.keySeparator?o.keySeparator:this.options.keySeparator,r=void 0!==o.ignoreJSONStructure?o.ignoreJSONStructure:this.options.ignoreJSONStructure;let a;t.indexOf(".")>-1?a=t.split("."):(a=[t,s],i&&(Array.isArray(i)?a.push(...i):e(i)&&n?a.push(...i.split(n)):a.push(i)));const h=l(this.data,a);return!h&&!s&&!i&&t.indexOf(".")>-1&&(t=a[0],s=a[1],i=a.slice(2).join(".")),!h&&r&&e(i)?f(this.data?.[t]?.[s],i,n):h}addResource(e,t,s,i){let o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{silent:!1};const n=void 0!==o.keySeparator?o.keySeparator:this.options.keySeparator;let r=[e,t];s&&(r=r.concat(n?s.split(n):s)),e.indexOf(".")>-1&&(r=e.split("."),i=t,t=r[1]),this.addNamespaces(t),a(this.data,r,i),o.silent||this.emit("added",e,t,s,i)}addResources(t,s,i){let o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{silent:!1};for(const o in i)(e(i[o])||Array.isArray(i[o]))&&this.addResource(t,s,o,i[o],{silent:!0});o.silent||this.emit("added",t,s,i)}addResourceBundle(e,t,s,i,o){let n=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{silent:!1,skipCopy:!1},r=[e,t];e.indexOf(".")>-1&&(r=e.split("."),i=s,s=t,t=r[1]),this.addNamespaces(t);let u=l(this.data,r)||{};n.skipCopy||(s=JSON.parse(JSON.stringify(s))),i?h(u,s,o):u={...u,...s},a(this.data,r,u),n.silent||this.emit("added",e,t,s)}removeResourceBundle(e,t){this.hasResourceBundle(e,t)&&delete this.data[e][t],this.removeNamespaces(t),this.emit("removed",e,t)}hasResourceBundle(e,t){return void 0!==this.getResource(e,t)}getResourceBundle(e,t){return t||(t=this.options.defaultNS),this.getResource(e,t)}getDataByLanguage(e){return this.data[e]}hasLanguageSomeTranslations(e){const t=this.getDataByLanguage(e);return!!(t&&Object.keys(t)||[]).find((e=>t[e]&&Object.keys(t[e]).length>0))}toJSON(){return this.data}}var S={processors:{},addPostProcessor(e){this.processors[e.name]=e},handle(e,t,s,i,o){return e.forEach((e=>{t=this.processors[e]?.process(t,s,i,o)??t})),t}};const O={},L=t=>!e(t)&&"boolean"!=typeof t&&"number"!=typeof t;class w extends b{constructor(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};var s,i;super(),s=e,i=this,["resourceStore","languageUtils","pluralResolver","interpolator","backendConnector","i18nFormat","utils"].forEach((e=>{s[e]&&(i[e]=s[e])})),this.options=t,void 0===this.options.keySeparator&&(this.options.keySeparator="."),this.logger=x.create("translator")}changeLanguage(e){e&&(this.language=e)}exists(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{interpolation:{}};if(null==e)return!1;const s=this.resolve(e,t);return void 0!==s?.res}extractFromKey(t,s){let i=void 0!==s.nsSeparator?s.nsSeparator:this.options.nsSeparator;void 0===i&&(i=":");const o=void 0!==s.keySeparator?s.keySeparator:this.options.keySeparator;let n=s.ns||this.options.defaultNS||[];const r=i&&t.indexOf(i)>-1,a=!(this.options.userDefinedKeySeparator||s.keySeparator||this.options.userDefinedNsSeparator||s.nsSeparator||((e,t,s)=>{t=t||"",s=s||"";const i=g.filter((e=>t.indexOf(e)<0&&s.indexOf(e)<0));if(0===i.length)return!0;const o=d.getRegExp(`(${i.map((e=>"?"===e?"\\?":e)).join("|")})`);let n=!o.test(e);if(!n){const t=e.indexOf(s);t>0&&!o.test(e.substring(0,t))&&(n=!0)}return n})(t,i,o));if(r&&!a){const s=t.match(this.interpolator.nestingRegexp);if(s&&s.length>0)return{key:t,namespaces:e(n)?[n]:n};const r=t.split(i);(i!==o||i===o&&this.options.ns.indexOf(r[0])>-1)&&(n=r.shift()),t=r.join(o)}return{key:t,namespaces:e(n)?[n]:n}}translate(t,s,i){if("object"!=typeof s&&this.options.overloadTranslationOptionHandler&&(s=this.options.overloadTranslationOptionHandler(arguments)),"object"==typeof s&&(s={...s}),s||(s={}),null==t)return"";Array.isArray(t)||(t=[String(t)]);const o=void 0!==s.returnDetails?s.returnDetails:this.options.returnDetails,n=void 0!==s.keySeparator?s.keySeparator:this.options.keySeparator,{key:r,namespaces:a}=this.extractFromKey(t[t.length-1],s),l=a[a.length-1],h=s.lng||this.language,u=s.appendNamespaceToCIMode||this.options.appendNamespaceToCIMode;if("cimode"===h?.toLowerCase()){if(u){const e=s.nsSeparator||this.options.nsSeparator;return o?{res:`${l}${e}${r}`,usedKey:r,exactUsedKey:r,usedLng:h,usedNS:l,usedParams:this.getUsedParamsDetails(s)}:`${l}${e}${r}`}return o?{res:r,usedKey:r,exactUsedKey:r,usedLng:h,usedNS:l,usedParams:this.getUsedParamsDetails(s)}:r}const c=this.resolve(t,s);let p=c?.res;const g=c?.usedKey||r,d=c?.exactUsedKey||r,f=void 0!==s.joinArrays?s.joinArrays:this.options.joinArrays,m=!this.i18nFormat||this.i18nFormat.handleAsObject,y=void 0!==s.count&&!e(s.count),v=w.hasDefaultValue(s),x=y?this.pluralResolver.getSuffix(h,s.count,s):"",b=s.ordinal&&y?this.pluralResolver.getSuffix(h,s.count,{ordinal:!1}):"",k=y&&!s.ordinal&&0===s.count,S=k&&s[`defaultValue${this.options.pluralSeparator}zero`]||s[`defaultValue${x}`]||s[`defaultValue${b}`]||s.defaultValue;let O=p;m&&!p&&v&&(O=S);const $=L(O),R=Object.prototype.toString.apply(O);if(!(m&&O&&$&&["[object Number]","[object Function]","[object RegExp]"].indexOf(R)<0)||e(f)&&Array.isArray(O))if(m&&e(f)&&Array.isArray(p))p=p.join(f),p&&(p=this.extendTranslation(p,t,s,i));else{let e=!1,o=!1;!this.isValidLookup(p)&&v&&(e=!0,p=S),this.isValidLookup(p)||(o=!0,p=r);const a=(s.missingKeyNoValueFallbackToKey||this.options.missingKeyNoValueFallbackToKey)&&o?void 0:p,u=v&&S!==p&&this.options.updateMissing;if(o||e||u){if(this.logger.log(u?"updateKey":"missingKey",h,l,r,u?S:p),n){const e=this.resolve(r,{...s,keySeparator:!1});e&&e.res&&this.logger.warn("Seems the loaded translations were in flat JSON format instead of nested. Either set keySeparator: false on init or make sure your translations are published in nested format.")}let e=[];const t=this.languageUtils.getFallbackCodes(this.options.fallbackLng,s.lng||this.language);if("fallback"===this.options.saveMissingTo&&t&&t[0])for(let s=0;s{const o=v&&i!==p?i:a;this.options.missingKeyHandler?this.options.missingKeyHandler(e,l,t,o,u,s):this.backendConnector?.saveMissing&&this.backendConnector.saveMissing(e,l,t,o,u,s),this.emit("missingKey",e,l,t,p)};this.options.saveMissing&&(this.options.saveMissingPlurals&&y?e.forEach((e=>{const t=this.pluralResolver.getSuffixes(e,s);k&&s[`defaultValue${this.options.pluralSeparator}zero`]&&t.indexOf(`${this.options.pluralSeparator}zero`)<0&&t.push(`${this.options.pluralSeparator}zero`),t.forEach((t=>{i([e],r+t,s[`defaultValue${t}`]||S)}))})):i(e,r,S))}p=this.extendTranslation(p,t,s,c,i),o&&p===r&&this.options.appendNamespaceToMissingKey&&(p=`${l}:${r}`),(o||e)&&this.options.parseMissingKeyHandler&&(p=this.options.parseMissingKeyHandler(this.options.appendNamespaceToMissingKey?`${l}:${r}`:r,e?p:void 0))}else{if(!s.returnObjects&&!this.options.returnObjects){this.options.returnedObjectHandler||this.logger.warn("accessing an object - but returnObjects options is not enabled!");const e=this.options.returnedObjectHandler?this.options.returnedObjectHandler(g,O,{...s,ns:a}):`key '${r} (${this.language})' returned an object instead of string.`;return o?(c.res=e,c.usedParams=this.getUsedParamsDetails(s),c):e}if(n){const e=Array.isArray(O),t=e?[]:{},i=e?d:g;for(const e in O)if(Object.prototype.hasOwnProperty.call(O,e)){const o=`${i}${n}${e}`;t[e]=v&&!p?this.translate(o,{...s,defaultValue:L(S)?S[e]:void 0,joinArrays:!1,ns:a}):this.translate(o,{...s,joinArrays:!1,ns:a}),t[e]===o&&(t[e]=O[e])}p=t}}return o?(c.res=p,c.usedParams=this.getUsedParamsDetails(s),c):p}extendTranslation(t,s,i,o,n){var r=this;if(this.i18nFormat?.parse)t=this.i18nFormat.parse(t,{...this.options.interpolation.defaultVariables,...i},i.lng||this.language||o.usedLng,o.usedNS,o.usedKey,{resolved:o});else if(!i.skipInterpolation){i.interpolation&&this.interpolator.init({...i,interpolation:{...this.options.interpolation,...i.interpolation}});const a=e(t)&&(void 0!==i?.interpolation?.skipOnVariables?i.interpolation.skipOnVariables:this.options.interpolation.skipOnVariables);let l;if(a){const e=t.match(this.interpolator.nestingRegexp);l=e&&e.length}let h=i.replace&&!e(i.replace)?i.replace:i;if(this.options.interpolation.defaultVariables&&(h={...this.options.interpolation.defaultVariables,...h}),t=this.interpolator.interpolate(t,h,i.lng||this.language||o.usedLng,i),a){const e=t.match(this.interpolator.nestingRegexp);l<(e&&e.length)&&(i.nest=!1)}!i.lng&&o&&o.res&&(i.lng=this.language||o.usedLng),!1!==i.nest&&(t=this.interpolator.nest(t,(function(){for(var e=arguments.length,t=new Array(e),o=0;o1&&void 0!==arguments[1]?arguments[1]:{};return e(t)&&(t=[t]),t.forEach((t=>{if(this.isValidLookup(s))return;const l=this.extractFromKey(t,a),h=l.key;i=h;let u=l.namespaces;this.options.fallbackNS&&(u=u.concat(this.options.fallbackNS));const c=void 0!==a.count&&!e(a.count),p=c&&!a.ordinal&&0===a.count,g=void 0!==a.context&&(e(a.context)||"number"==typeof a.context)&&""!==a.context,d=a.lngs?a.lngs:this.languageUtils.toResolveHierarchy(a.lng||this.language,a.fallbackLng);u.forEach((e=>{this.isValidLookup(s)||(r=e,O[`${d[0]}-${e}`]||!this.utils?.hasLoadedNamespace||this.utils?.hasLoadedNamespace(r)||(O[`${d[0]}-${e}`]=!0,this.logger.warn(`key "${i}" for languages "${d.join(", ")}" won't get resolved as namespace "${r}" was not yet loaded`,"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!")),d.forEach((t=>{if(this.isValidLookup(s))return;n=t;const i=[h];if(this.i18nFormat?.addLookupKeys)this.i18nFormat.addLookupKeys(i,h,t,e,a);else{let e;c&&(e=this.pluralResolver.getSuffix(t,a.count,a));const s=`${this.options.pluralSeparator}zero`,o=`${this.options.pluralSeparator}ordinal${this.options.pluralSeparator}`;if(c&&(i.push(h+e),a.ordinal&&0===e.indexOf(o)&&i.push(h+e.replace(o,this.options.pluralSeparator)),p&&i.push(h+s)),g){const t=`${h}${this.options.contextSeparator}${a.context}`;i.push(t),c&&(i.push(t+e),a.ordinal&&0===e.indexOf(o)&&i.push(t+e.replace(o,this.options.pluralSeparator)),p&&i.push(t+s))}}let r;for(;r=i.pop();)this.isValidLookup(s)||(o=r,s=this.getResource(t,e,r,a))})))}))})),{res:s,usedKey:i,exactUsedKey:o,usedLng:n,usedNS:r}}isValidLookup(e){return!(void 0===e||!this.options.returnNull&&null===e||!this.options.returnEmptyString&&""===e)}getResource(e,t,s){let i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};return this.i18nFormat?.getResource?this.i18nFormat.getResource(e,t,s,i):this.resourceStore.getResource(e,t,s,i)}getUsedParamsDetails(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const s=["defaultValue","ordinal","context","replace","lng","lngs","fallbackLng","ns","keySeparator","nsSeparator","returnObjects","returnDetails","joinArrays","postProcess","interpolation"],i=t.replace&&!e(t.replace);let o=i?t.replace:t;if(i&&void 0!==t.count&&(o.count=t.count),this.options.interpolation.defaultVariables&&(o={...this.options.interpolation.defaultVariables,...o}),!i){o={...o};for(const e of s)delete o[e]}return o}static hasDefaultValue(e){const t="defaultValue";for(const s in e)if(Object.prototype.hasOwnProperty.call(e,s)&&t===s.substring(0,12)&&void 0!==e[s])return!0;return!1}}class ${constructor(e){this.options=e,this.supportedLngs=this.options.supportedLngs||!1,this.logger=x.create("languageUtils")}getScriptPartFromCode(e){if(!(e=m(e))||e.indexOf("-")<0)return null;const t=e.split("-");return 2===t.length?null:(t.pop(),"x"===t[t.length-1].toLowerCase()?null:this.formatLanguageCode(t.join("-")))}getLanguagePartFromCode(e){if(!(e=m(e))||e.indexOf("-")<0)return e;const t=e.split("-");return this.formatLanguageCode(t[0])}formatLanguageCode(t){if(e(t)&&t.indexOf("-")>-1){let e;try{e=Intl.getCanonicalLocales(t)[0]}catch(e){}return e&&this.options.lowerCaseLng&&(e=e.toLowerCase()),e||(this.options.lowerCaseLng?t.toLowerCase():t)}return this.options.cleanCode||this.options.lowerCaseLng?t.toLowerCase():t}isSupportedCode(e){return("languageOnly"===this.options.load||this.options.nonExplicitSupportedLngs)&&(e=this.getLanguagePartFromCode(e)),!this.supportedLngs||!this.supportedLngs.length||this.supportedLngs.indexOf(e)>-1}getBestMatchFromCodes(e){if(!e)return null;let t;return e.forEach((e=>{if(t)return;const s=this.formatLanguageCode(e);this.options.supportedLngs&&!this.isSupportedCode(s)||(t=s)})),!t&&this.options.supportedLngs&&e.forEach((e=>{if(t)return;const s=this.getLanguagePartFromCode(e);if(this.isSupportedCode(s))return t=s;t=this.options.supportedLngs.find((e=>e===s?e:e.indexOf("-")<0&&s.indexOf("-")<0?void 0:e.indexOf("-")>0&&s.indexOf("-")<0&&e.substring(0,e.indexOf("-"))===s||0===e.indexOf(s)&&s.length>1?e:void 0))})),t||(t=this.getFallbackCodes(this.options.fallbackLng)[0]),t}getFallbackCodes(t,s){if(!t)return[];if("function"==typeof t&&(t=t(s)),e(t)&&(t=[t]),Array.isArray(t))return t;if(!s)return t.default||[];let i=t[s];return i||(i=t[this.getScriptPartFromCode(s)]),i||(i=t[this.formatLanguageCode(s)]),i||(i=t[this.getLanguagePartFromCode(s)]),i||(i=t.default),i||[]}toResolveHierarchy(t,s){const i=this.getFallbackCodes(s||this.options.fallbackLng||[],t),o=[],n=e=>{e&&(this.isSupportedCode(e)?o.push(e):this.logger.warn(`rejecting language code not found in supportedLngs: ${e}`))};return e(t)&&(t.indexOf("-")>-1||t.indexOf("_")>-1)?("languageOnly"!==this.options.load&&n(this.formatLanguageCode(t)),"languageOnly"!==this.options.load&&"currentOnly"!==this.options.load&&n(this.getScriptPartFromCode(t)),"currentOnly"!==this.options.load&&n(this.getLanguagePartFromCode(t))):e(t)&&n(this.formatLanguageCode(t)),i.forEach((e=>{o.indexOf(e)<0&&n(this.formatLanguageCode(e))})),o}}const R={zero:0,one:1,two:2,few:3,many:4,other:5},C={select:e=>1===e?"one":"other",resolvedOptions:()=>({pluralCategories:["one","other"]})};class P{constructor(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.languageUtils=e,this.options=t,this.logger=x.create("pluralResolver"),this.pluralRulesCache={}}addRule(e,t){this.rules[e]=t}clearCache(){this.pluralRulesCache={}}getRule(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const s=m("dev"===e?"en":e),i=t.ordinal?"ordinal":"cardinal",o=JSON.stringify({cleanedCode:s,type:i});if(o in this.pluralRulesCache)return this.pluralRulesCache[o];let n;try{n=new Intl.PluralRules(s,{type:i})}catch(s){if(!Intl)return this.logger.error("No Intl support, please use an Intl polyfill!"),C;if(!e.match(/-|_/))return C;const i=this.languageUtils.getLanguagePartFromCode(e);n=this.getRule(i,t)}return this.pluralRulesCache[o]=n,n}needsPlural(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},s=this.getRule(e,t);return s||(s=this.getRule("dev",t)),s?.resolvedOptions().pluralCategories.length>1}getPluralFormsOfKey(e,t){let s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return this.getSuffixes(e,s).map((e=>`${t}${e}`))}getSuffixes(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},s=this.getRule(e,t);return s||(s=this.getRule("dev",t)),s?s.resolvedOptions().pluralCategories.sort(((e,t)=>R[e]-R[t])).map((e=>`${this.options.prepend}${t.ordinal?`ordinal${this.options.prepend}`:""}${e}`)):[]}getSuffix(e,t){let s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const i=this.getRule(e,s);return i?`${this.options.prepend}${s.ordinal?`ordinal${this.options.prepend}`:""}${i.select(t)}`:(this.logger.warn(`no plural rule found for: ${e}`),this.getSuffix("dev",t,s))}}const N=function(t,s,i){let o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:".",n=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],r=((e,t,s)=>{const i=l(e,s);return void 0!==i?i:l(t,s)})(t,s,i);return!r&&n&&e(i)&&(r=f(t,i,o),void 0===r&&(r=f(s,i,o))),r},j=e=>e.replace(/\$/g,"$$$$");class E{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.logger=x.create("interpolator"),this.options=e,this.format=e?.interpolation?.format||(e=>e),this.init(e)}init(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};e.interpolation||(e.interpolation={escapeValue:!0});const{escape:t,escapeValue:s,useRawValueToEscape:i,prefix:o,prefixEscaped:n,suffix:r,suffixEscaped:a,formatSeparator:l,unescapeSuffix:h,unescapePrefix:c,nestingPrefix:g,nestingPrefixEscaped:d,nestingSuffix:f,nestingSuffixEscaped:m,nestingOptionsSeparator:y,maxReplaces:v,alwaysFormat:x}=e.interpolation;this.escape=void 0!==t?t:p,this.escapeValue=void 0===s||s,this.useRawValueToEscape=void 0!==i&&i,this.prefix=o?u(o):n||"{{",this.suffix=r?u(r):a||"}}",this.formatSeparator=l||",",this.unescapePrefix=h?"":c||"-",this.unescapeSuffix=this.unescapePrefix?"":h||"",this.nestingPrefix=g?u(g):d||u("$t("),this.nestingSuffix=f?u(f):m||u(")"),this.nestingOptionsSeparator=y||",",this.maxReplaces=v||1e3,this.alwaysFormat=void 0!==x&&x,this.resetRegExp()}reset(){this.options&&this.init(this.options)}resetRegExp(){const e=(e,t)=>e?.source===t?(e.lastIndex=0,e):new RegExp(t,"g");this.regexp=e(this.regexp,`${this.prefix}(.+?)${this.suffix}`),this.regexpUnescape=e(this.regexpUnescape,`${this.prefix}${this.unescapePrefix}(.+?)${this.unescapeSuffix}${this.suffix}`),this.nestingRegexp=e(this.nestingRegexp,`${this.nestingPrefix}(.+?)${this.nestingSuffix}`)}interpolate(t,i,o,n){let r,a,l;const h=this.options&&this.options.interpolation&&this.options.interpolation.defaultVariables||{},u=e=>{if(e.indexOf(this.formatSeparator)<0){const t=N(i,h,e,this.options.keySeparator,this.options.ignoreJSONStructure);return this.alwaysFormat?this.format(t,void 0,o,{...n,...i,interpolationkey:e}):t}const t=e.split(this.formatSeparator),s=t.shift().trim(),r=t.join(this.formatSeparator).trim();return this.format(N(i,h,s,this.options.keySeparator,this.options.ignoreJSONStructure),r,o,{...n,...i,interpolationkey:s})};this.resetRegExp();const c=n?.missingInterpolationHandler||this.options.missingInterpolationHandler,p=void 0!==n?.interpolation?.skipOnVariables?n.interpolation.skipOnVariables:this.options.interpolation.skipOnVariables;return[{regex:this.regexpUnescape,safeValue:e=>j(e)},{regex:this.regexp,safeValue:e=>this.escapeValue?j(this.escape(e)):j(e)}].forEach((i=>{for(l=0;r=i.regex.exec(t);){const o=r[1].trim();if(a=u(o),void 0===a)if("function"==typeof c){const s=c(t,r,n);a=e(s)?s:""}else if(n&&Object.prototype.hasOwnProperty.call(n,o))a="";else{if(p){a=r[0];continue}this.logger.warn(`missed to pass in variable ${o} for interpolating ${t}`),a=""}else e(a)||this.useRawValueToEscape||(a=s(a));const h=i.safeValue(a);if(t=t.replace(r[0],h),p?(i.regex.lastIndex+=a.length,i.regex.lastIndex-=r[0].length):i.regex.lastIndex=0,l++,l>=this.maxReplaces)break}})),t}nest(t,i){let o,n,r,a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const l=(e,t)=>{const s=this.nestingOptionsSeparator;if(e.indexOf(s)<0)return e;const i=e.split(new RegExp(`${s}[ ]*{`));let o=`{${i[1]}`;e=i[0],o=this.interpolate(o,r);const n=o.match(/'/g),a=o.match(/"/g);((n?.length??0)%2==0&&!a||a.length%2!=0)&&(o=o.replace(/'/g,'"'));try{r=JSON.parse(o),t&&(r={...t,...r})}catch(t){return this.logger.warn(`failed parsing options string in nesting for key ${e}`,t),`${e}${s}${o}`}return r.defaultValue&&r.defaultValue.indexOf(this.prefix)>-1&&delete r.defaultValue,e};for(;o=this.nestingRegexp.exec(t);){let h=[];r={...a},r=r.replace&&!e(r.replace)?r.replace:r,r.applyPostProcessor=!1,delete r.defaultValue;let u=!1;if(-1!==o[0].indexOf(this.formatSeparator)&&!/{.*}/.test(o[1])){const e=o[1].split(this.formatSeparator).map((e=>e.trim()));o[1]=e.shift(),h=e,u=!0}if(n=i(l.call(this,o[1].trim(),r),r),n&&o[0]===t&&!e(n))return n;e(n)||(n=s(n)),n||(this.logger.warn(`missed to resolve ${o[1]} for nesting ${t}`),n=""),u&&(n=h.reduce(((e,t)=>this.format(e,t,a.lng,{...a,interpolationkey:o[1].trim()})),n.trim())),t=t.replace(o[0],n),this.regexp.lastIndex=0}return t}}const F=e=>{const t={};return(s,i,o)=>{let n=o;o&&o.interpolationkey&&o.formatParams&&o.formatParams[o.interpolationkey]&&o[o.interpolationkey]&&(n={...n,[o.interpolationkey]:void 0});const r=i+JSON.stringify(n);let a=t[r];return a||(a=e(m(i),o),t[r]=a),a(s)}};class V{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.logger=x.create("formatter"),this.options=e,this.formats={number:F(((e,t)=>{const s=new Intl.NumberFormat(e,{...t});return e=>s.format(e)})),currency:F(((e,t)=>{const s=new Intl.NumberFormat(e,{...t,style:"currency"});return e=>s.format(e)})),datetime:F(((e,t)=>{const s=new Intl.DateTimeFormat(e,{...t});return e=>s.format(e)})),relativetime:F(((e,t)=>{const s=new Intl.RelativeTimeFormat(e,{...t});return e=>s.format(e,t.range||"day")})),list:F(((e,t)=>{const s=new Intl.ListFormat(e,{...t});return e=>s.format(e)}))},this.init(e)}init(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{interpolation:{}};this.formatSeparator=t.interpolation.formatSeparator||","}add(e,t){this.formats[e.toLowerCase().trim()]=t}addCached(e,t){this.formats[e.toLowerCase().trim()]=F(t)}format(e,t,s){let i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};const o=t.split(this.formatSeparator);if(o.length>1&&o[0].indexOf("(")>1&&o[0].indexOf(")")<0&&o.find((e=>e.indexOf(")")>-1))){const e=o.findIndex((e=>e.indexOf(")")>-1));o[0]=[o[0],...o.splice(1,e)].join(this.formatSeparator)}return o.reduce(((e,t)=>{const{formatName:o,formatOptions:n}=(e=>{let t=e.toLowerCase().trim();const s={};if(e.indexOf("(")>-1){const i=e.split("(");t=i[0].toLowerCase().trim();const o=i[1].substring(0,i[1].length-1);"currency"===t&&o.indexOf(":")<0?s.currency||(s.currency=o.trim()):"relativetime"===t&&o.indexOf(":")<0?s.range||(s.range=o.trim()):o.split(";").forEach((e=>{if(e){const[t,...i]=e.split(":"),o=i.join(":").trim().replace(/^'+|'+$/g,""),n=t.trim();s[n]||(s[n]=o),"false"===o&&(s[n]=!1),"true"===o&&(s[n]=!0),isNaN(o)||(s[n]=parseInt(o,10))}}))}return{formatName:t,formatOptions:s}})(t);if(this.formats[o]){let t=e;try{const r=i?.formatParams?.[i.interpolationkey]||{},a=r.locale||r.lng||i.locale||i.lng||s;t=this.formats[o](e,a,{...n,...i,...r})}catch(e){this.logger.warn(e)}return t}return this.logger.warn(`there was no format function for ${o}`),e}),e)}}class A extends b{constructor(e,t,s){let i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};super(),this.backend=e,this.store=t,this.services=s,this.languageUtils=s.languageUtils,this.options=i,this.logger=x.create("backendConnector"),this.waitingReads=[],this.maxParallelReads=i.maxParallelReads||10,this.readingCalls=0,this.maxRetries=i.maxRetries>=0?i.maxRetries:5,this.retryTimeout=i.retryTimeout>=1?i.retryTimeout:350,this.state={},this.queue=[],this.backend?.init?.(s,i.backend,i)}queueLoad(e,t,s,i){const o={},n={},r={},a={};return e.forEach((e=>{let i=!0;t.forEach((t=>{const r=`${e}|${t}`;!s.reload&&this.store.hasResourceBundle(e,t)?this.state[r]=2:this.state[r]<0||(1===this.state[r]?void 0===n[r]&&(n[r]=!0):(this.state[r]=1,i=!1,void 0===n[r]&&(n[r]=!0),void 0===o[r]&&(o[r]=!0),void 0===a[t]&&(a[t]=!0)))})),i||(r[e]=!0)})),(Object.keys(o).length||Object.keys(n).length)&&this.queue.push({pending:n,pendingCount:Object.keys(n).length,loaded:{},errors:[],callback:i}),{toLoad:Object.keys(o),pending:Object.keys(n),toLoadLanguages:Object.keys(r),toLoadNamespaces:Object.keys(a)}}loaded(e,t,s){const i=e.split("|"),o=i[0],n=i[1];t&&this.emit("failedLoading",o,n,t),!t&&s&&this.store.addResourceBundle(o,n,s,void 0,void 0,{skipCopy:!0}),this.state[e]=t?-1:2,t&&s&&(this.state[e]=0);const a={};this.queue.forEach((s=>{((e,t,s,i)=>{const{obj:o,k:n}=r(e,t,Object);o[n]=o[n]||[],o[n].push(s)})(s.loaded,[o],n),((e,t)=>{void 0!==e.pending[t]&&(delete e.pending[t],e.pendingCount--)})(s,e),t&&s.errors.push(t),0!==s.pendingCount||s.done||(Object.keys(s.loaded).forEach((e=>{a[e]||(a[e]={});const t=s.loaded[e];t.length&&t.forEach((t=>{void 0===a[e][t]&&(a[e][t]=!0)}))})),s.done=!0,s.errors.length?s.callback(s.errors):s.callback())})),this.emit("loaded",a),this.queue=this.queue.filter((e=>!e.done))}read(e,t,s){let i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:this.retryTimeout,n=arguments.length>5?arguments[5]:void 0;if(!e.length)return n(null,{});if(this.readingCalls>=this.maxParallelReads)return void this.waitingReads.push({lng:e,ns:t,fcName:s,tried:i,wait:o,callback:n});this.readingCalls++;const r=(r,a)=>{if(this.readingCalls--,this.waitingReads.length>0){const e=this.waitingReads.shift();this.read(e.lng,e.ns,e.fcName,e.tried,e.wait,e.callback)}r&&a&&i{this.read.call(this,e,t,s,i+1,2*o,n)}),o):n(r,a)},a=this.backend[s].bind(this.backend);if(2!==a.length)return a(e,t,r);try{const s=a(e,t);s&&"function"==typeof s.then?s.then((e=>r(null,e))).catch(r):r(null,s)}catch(e){r(e)}}prepareLoading(t,s){let i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o=arguments.length>3?arguments[3]:void 0;if(!this.backend)return this.logger.warn("No backend was added via i18next.use. Will not load resources."),o&&o();e(t)&&(t=this.languageUtils.toResolveHierarchy(t)),e(s)&&(s=[s]);const n=this.queueLoad(t,s,i,o);if(!n.toLoad.length)return n.pending.length||o(),null;n.toLoad.forEach((e=>{this.loadOne(e)}))}load(e,t,s){this.prepareLoading(e,t,{},s)}reload(e,t,s){this.prepareLoading(e,t,{reload:!0},s)}loadOne(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";const s=e.split("|"),i=s[0],o=s[1];this.read(i,o,"read",void 0,void 0,((s,n)=>{s&&this.logger.warn(`${t}loading namespace ${o} for language ${i} failed`,s),!s&&n&&this.logger.log(`${t}loaded namespace ${o} for language ${i}`,n),this.loaded(e,s,n)}))}saveMissing(e,t,s,i,o){let n=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{},r=arguments.length>6&&void 0!==arguments[6]?arguments[6]:()=>{};if(!this.services?.utils?.hasLoadedNamespace||this.services?.utils?.hasLoadedNamespace(t)){if(null!=s&&""!==s){if(this.backend?.create){const a={...n,isUpdate:o},l=this.backend.create.bind(this.backend);if(l.length<6)try{let o;o=5===l.length?l(e,t,s,i,a):l(e,t,s,i),o&&"function"==typeof o.then?o.then((e=>r(null,e))).catch(r):r(null,o)}catch(e){r(e)}else l(e,t,s,i,r,a)}e&&e[0]&&this.store.addResource(e[0],t,s,i)}}else this.logger.warn(`did not save key "${s}" as the namespace "${t}" was not yet loaded`,"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!")}}const D=()=>({debug:!1,initAsync:!0,ns:["translation"],defaultNS:["translation"],fallbackLng:["dev"],fallbackNS:!1,supportedLngs:!1,nonExplicitSupportedLngs:!1,load:"all",preload:!1,simplifyPluralSuffix:!0,keySeparator:".",nsSeparator:":",pluralSeparator:"_",contextSeparator:"_",partialBundledLanguages:!1,saveMissing:!1,updateMissing:!1,saveMissingTo:"fallback",saveMissingPlurals:!0,missingKeyHandler:!1,missingInterpolationHandler:!1,postProcess:!1,postProcessPassResolved:!1,returnNull:!1,returnEmptyString:!0,returnObjects:!1,joinArrays:!1,returnedObjectHandler:!1,parseMissingKeyHandler:!1,appendNamespaceToMissingKey:!1,appendNamespaceToCIMode:!1,overloadTranslationOptionHandler:t=>{let s={};if("object"==typeof t[1]&&(s=t[1]),e(t[1])&&(s.defaultValue=t[1]),e(t[2])&&(s.tDescription=t[2]),"object"==typeof t[2]||"object"==typeof t[3]){const e=t[3]||t[2];Object.keys(e).forEach((t=>{s[t]=e[t]}))}return s},interpolation:{escapeValue:!0,format:e=>e,prefix:"{{",suffix:"}}",formatSeparator:",",unescapePrefix:"-",nestingPrefix:"$t(",nestingSuffix:")",nestingOptionsSeparator:",",maxReplaces:1e3,skipOnVariables:!0}}),I=t=>(e(t.ns)&&(t.ns=[t.ns]),e(t.fallbackLng)&&(t.fallbackLng=[t.fallbackLng]),e(t.fallbackNS)&&(t.fallbackNS=[t.fallbackNS]),t.supportedLngs?.indexOf?.("cimode")<0&&(t.supportedLngs=t.supportedLngs.concat(["cimode"])),"boolean"==typeof t.initImmediate&&(t.initAsync=t.initImmediate),t),T=()=>{};class K extends b{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1?arguments[1]:void 0;var s;if(super(),this.options=I(e),this.services={},this.logger=x,this.modules={external:[]},s=this,Object.getOwnPropertyNames(Object.getPrototypeOf(s)).forEach((e=>{"function"==typeof s[e]&&(s[e]=s[e].bind(s))})),t&&!this.isInitialized&&!e.isClone){if(!this.options.initAsync)return this.init(e,t),this;setTimeout((()=>{this.init(e,t)}),0)}}init(){var s=this;let i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},o=arguments.length>1?arguments[1]:void 0;this.isInitializing=!0,"function"==typeof i&&(o=i,i={}),null==i.defaultNS&&i.ns&&(e(i.ns)?i.defaultNS=i.ns:i.ns.indexOf("translation")<0&&(i.defaultNS=i.ns[0]));const n=D();this.options={...n,...this.options,...I(i)},this.options.interpolation={...n.interpolation,...this.options.interpolation},void 0!==i.keySeparator&&(this.options.userDefinedKeySeparator=i.keySeparator),void 0!==i.nsSeparator&&(this.options.userDefinedNsSeparator=i.nsSeparator);const r=e=>e?"function"==typeof e?new e:e:null;if(!this.options.isClone){let e;this.modules.logger?x.init(r(this.modules.logger),this.options):x.init(null,this.options),e=this.modules.formatter?this.modules.formatter:V;const t=new $(this.options);this.store=new k(this.options.resources,this.options);const i=this.services;i.logger=x,i.resourceStore=this.store,i.languageUtils=t,i.pluralResolver=new P(t,{prepend:this.options.pluralSeparator,simplifyPluralSuffix:this.options.simplifyPluralSuffix}),!e||this.options.interpolation.format&&this.options.interpolation.format!==n.interpolation.format||(i.formatter=r(e),i.formatter.init(i,this.options),this.options.interpolation.format=i.formatter.format.bind(i.formatter)),i.interpolator=new E(this.options),i.utils={hasLoadedNamespace:this.hasLoadedNamespace.bind(this)},i.backendConnector=new A(r(this.modules.backend),i.resourceStore,i,this.options),i.backendConnector.on("*",(function(e){for(var t=arguments.length,i=new Array(t>1?t-1:0),o=1;o1?t-1:0),o=1;o{e.init&&e.init(this)}))}if(this.format=this.options.interpolation.format,o||(o=T),this.options.fallbackLng&&!this.services.languageDetector&&!this.options.lng){const e=this.services.languageUtils.getFallbackCodes(this.options.fallbackLng);e.length>0&&"dev"!==e[0]&&(this.options.lng=e[0])}this.services.languageDetector||this.options.lng||this.logger.warn("init: no languageDetector is used and no lng is defined");["getResource","hasResourceBundle","getResourceBundle","getDataByLanguage"].forEach((e=>{this[e]=function(){return s.store[e](...arguments)}}));["addResource","addResources","addResourceBundle","removeResourceBundle"].forEach((e=>{this[e]=function(){return s.store[e](...arguments),s}}));const a=t(),l=()=>{const e=(e,t)=>{this.isInitializing=!1,this.isInitialized&&!this.initializedStoreOnce&&this.logger.warn("init: i18next is already initialized. You should call init just once!"),this.isInitialized=!0,this.options.isClone||this.logger.log("initialized",this.options),this.emit("initialized",this.options),a.resolve(t),o(e,t)};if(this.languages&&!this.isInitialized)return e(null,this.t.bind(this));this.changeLanguage(this.options.lng,e)};return this.options.resources||!this.options.initAsync?l():setTimeout(l,0),a}loadResources(t){let s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:T;const i=e(t)?t:this.language;if("function"==typeof t&&(s=t),!this.options.resources||this.options.partialBundledLanguages){if("cimode"===i?.toLowerCase()&&(!this.options.preload||0===this.options.preload.length))return s();const e=[],t=t=>{if(!t)return;if("cimode"===t)return;this.services.languageUtils.toResolveHierarchy(t).forEach((t=>{"cimode"!==t&&e.indexOf(t)<0&&e.push(t)}))};if(i)t(i);else{this.services.languageUtils.getFallbackCodes(this.options.fallbackLng).forEach((e=>t(e)))}this.options.preload?.forEach?.((e=>t(e))),this.services.backendConnector.load(e,this.options.ns,(e=>{e||this.resolvedLanguage||!this.language||this.setResolvedLanguage(this.language),s(e)}))}else s(null)}reloadResources(e,s,i){const o=t();return"function"==typeof e&&(i=e,e=void 0),"function"==typeof s&&(i=s,s=void 0),e||(e=this.languages),s||(s=this.options.ns),i||(i=T),this.services.backendConnector.reload(e,s,(e=>{o.resolve(),i(e)})),o}use(e){if(!e)throw new Error("You are passing an undefined module! Please check the object you are passing to i18next.use()");if(!e.type)throw new Error("You are passing a wrong module! Please check the object you are passing to i18next.use()");return"backend"===e.type&&(this.modules.backend=e),("logger"===e.type||e.log&&e.warn&&e.error)&&(this.modules.logger=e),"languageDetector"===e.type&&(this.modules.languageDetector=e),"i18nFormat"===e.type&&(this.modules.i18nFormat=e),"postProcessor"===e.type&&S.addPostProcessor(e),"formatter"===e.type&&(this.modules.formatter=e),"3rdParty"===e.type&&this.modules.external.push(e),this}setResolvedLanguage(e){if(e&&this.languages&&!(["cimode","dev"].indexOf(e)>-1))for(let e=0;e-1)&&this.store.hasLanguageSomeTranslations(t)){this.resolvedLanguage=t;break}}}changeLanguage(s,i){var o=this;this.isLanguageChangingTo=s;const n=t();this.emit("languageChanging",s);const r=e=>{this.language=e,this.languages=this.services.languageUtils.toResolveHierarchy(e),this.resolvedLanguage=void 0,this.setResolvedLanguage(e)},a=(e,t)=>{t?(r(t),this.translator.changeLanguage(t),this.isLanguageChangingTo=void 0,this.emit("languageChanged",t),this.logger.log("languageChanged",t)):this.isLanguageChangingTo=void 0,n.resolve((function(){return o.t(...arguments)})),i&&i(e,(function(){return o.t(...arguments)}))},l=t=>{s||t||!this.services.languageDetector||(t=[]);const i=e(t)?t:this.services.languageUtils.getBestMatchFromCodes(t);i&&(this.language||r(i),this.translator.language||this.translator.changeLanguage(i),this.services.languageDetector?.cacheUserLanguage?.(i)),this.loadResources(i,(e=>{a(e,i)}))};return s||!this.services.languageDetector||this.services.languageDetector.async?!s&&this.services.languageDetector&&this.services.languageDetector.async?0===this.services.languageDetector.detect.length?this.services.languageDetector.detect().then(l):this.services.languageDetector.detect(l):l(s):l(this.services.languageDetector.detect()),n}getFixedT(t,s,i){var o=this;const n=function(e,t){let s;if("object"!=typeof t){for(var r=arguments.length,a=new Array(r>2?r-2:0),l=2;l`${s.keyPrefix}${h}${e}`)):s.keyPrefix?`${s.keyPrefix}${h}${e}`:e,o.t(u,s)};return e(t)?n.lng=t:n.lngs=t,n.ns=s,n.keyPrefix=i,n}t(){for(var e=arguments.length,t=new Array(e),s=0;s1&&void 0!==arguments[1]?arguments[1]:{};if(!this.isInitialized)return this.logger.warn("hasLoadedNamespace: i18next was not initialized",this.languages),!1;if(!this.languages||!this.languages.length)return this.logger.warn("hasLoadedNamespace: i18n.languages were undefined or empty",this.languages),!1;const s=t.lng||this.resolvedLanguage||this.languages[0],i=!!this.options&&this.options.fallbackLng,o=this.languages[this.languages.length-1];if("cimode"===s.toLowerCase())return!0;const n=(e,t)=>{const s=this.services.backendConnector.state[`${e}|${t}`];return-1===s||0===s||2===s};if(t.precheck){const e=t.precheck(this,n);if(void 0!==e)return e}return!!this.hasResourceBundle(s,e)||(!(this.services.backendConnector.backend&&(!this.options.resources||this.options.partialBundledLanguages))||!(!n(s,e)||i&&!n(o,e)))}loadNamespaces(s,i){const o=t();return this.options.ns?(e(s)&&(s=[s]),s.forEach((e=>{this.options.ns.indexOf(e)<0&&this.options.ns.push(e)})),this.loadResources((e=>{o.resolve(),i&&i(e)})),o):(i&&i(),Promise.resolve())}loadLanguages(s,i){const o=t();e(s)&&(s=[s]);const n=this.options.preload||[],r=s.filter((e=>n.indexOf(e)<0&&this.services.languageUtils.isSupportedCode(e)));return r.length?(this.options.preload=n.concat(r),this.loadResources((e=>{o.resolve(),i&&i(e)})),o):(i&&i(),Promise.resolve())}dir(e){if(e||(e=this.resolvedLanguage||(this.languages?.length>0?this.languages[0]:this.language)),!e)return"rtl";const t=this.services?.languageUtils||new $(D());return["ar","shu","sqr","ssh","xaa","yhd","yud","aao","abh","abv","acm","acq","acw","acx","acy","adf","ads","aeb","aec","afb","ajp","apc","apd","arb","arq","ars","ary","arz","auz","avl","ayh","ayl","ayn","ayp","bbz","pga","he","iw","ps","pbt","pbu","pst","prp","prd","ug","ur","ydd","yds","yih","ji","yi","hbo","men","xmn","fa","jpr","peo","pes","prs","dv","sam","ckb"].indexOf(t.getLanguagePartFromCode(e))>-1||e.toLowerCase().indexOf("-arab")>1?"rtl":"ltr"}static createInstance(){return new K(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},arguments.length>1?arguments[1]:void 0)}cloneInstance(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:T;const s=e.forkResourceStore;s&&delete e.forkResourceStore;const i={...this.options,...e,isClone:!0},o=new K(i);void 0===e.debug&&void 0===e.prefix||(o.logger=o.logger.clone(e));if(["store","services","language"].forEach((e=>{o[e]=this[e]})),o.services={...this.services},o.services.utils={hasLoadedNamespace:o.hasLoadedNamespace.bind(o)},s){const e=Object.keys(this.store.data).reduce(((e,t)=>(e[t]={...this.store.data[t]},Object.keys(e[t]).reduce(((s,i)=>(s[i]={...e[t][i]},s)),{}))),{});o.store=new k(e,i),o.services.resourceStore=o.store}return o.translator=new w(o.services,i),o.translator.on("*",(function(e){for(var t=arguments.length,s=new Array(t>1?t-1:0),i=1;i { @@ -110,17 +112,51 @@ single.on("route_load", function (event) { } }); -function setActiveLanguage(lang) { +async function setActiveLanguage(lang) { window.lang = lang; const lang_name = document.querySelector("[data-lang=" + lang + "]").textContent; document.getElementById("active-lang-name").textContent = lang_name; document.querySelector("[data-lang].active").classList.remove("active"); document.querySelector("[data-lang=" + lang + "]").classList.add("active"); + const response = await fetch(`/webui/translations/${lang}.json`); + const translations = await response.json(); + + i18next.init( + { + lng: lang, + fallbackLng: "en", + resources: { + [lang]: { + translation: translations + } + } + }, + function () { + document.querySelectorAll("[data-i18n]").forEach(element => { + const key = element.getAttribute("data-i18n"); + element.innerHTML = i18next.t(key); + const displayNameElement = element.querySelector(".displayname"); + if (displayNameElement) { + displayNameElement.textContent = localStorage.getItem("displayName"); + } + }); + + document.querySelectorAll("[data-i18n-placeholder]").forEach(element => { + const key = element.getAttribute("data-i18n-placeholder"); + element.setAttribute("placeholder", i18next.t(key)); + }); + + document.querySelectorAll("[data-i18n-value]").forEach(element => { + const key = element.getAttribute("data-i18n-value"); + element.setAttribute("value", i18next.t(key)); + }); + } + ); } setActiveLanguage(localStorage.getItem("lang") ?? "en"); -function setLanguage(lang) { - setActiveLanguage(lang); +async function setLanguage(lang) { + await setActiveLanguage(lang); localStorage.setItem("lang", lang); fetchItemList(); updateInventory(); @@ -135,30 +171,110 @@ function fetchItemList() { const itemMap = { // Generics for rivens - "/Lotus/Weapons/Tenno/Archwing/Primary/ArchGun": { name: "Archgun" }, - "/Lotus/Weapons/Tenno/Melee/PlayerMeleeWeapon": { name: "Melee" }, - "/Lotus/Weapons/Tenno/Pistol/LotusPistol": { name: "Pistol" }, - "/Lotus/Weapons/Tenno/Rifle/LotusRifle": { name: "Rifle" }, - "/Lotus/Weapons/Tenno/Shotgun/LotusShotgun": { name: "Shotgun" }, - // Modular weapons - "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary": { name: "Kitgun" }, - "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam": { name: "Kitgun" }, - "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryLauncher": { name: "Kitgun" }, - "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun": { name: "Kitgun" }, - "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimarySniper": { name: "Kitgun" }, - "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary": { name: "Kitgun" }, - "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam": { name: "Kitgun" }, - "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun": { name: "Kitgun" }, - "/Lotus/Weapons/Ostron/Melee/LotusModularWeapon": { name: "Zaw" }, - "/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/OperatorTrainingAmpWeapon": { - name: "Mote Amp" + "/Lotus/Weapons/Tenno/Archwing/Primary/ArchGun": { + get name() { + return i18next.t("code.archgun"); + } }, - "/Lotus/Weapons/Sentients/OperatorAmplifiers/OperatorAmpWeapon": { name: "Amp" }, - "/Lotus/Weapons/Operator/Pistols/DrifterPistol/DrifterPistolPlayerWeapon": { name: "Sirocco" }, - "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit": { name: "K-Drive" }, + "/Lotus/Weapons/Tenno/Melee/PlayerMeleeWeapon": { + get name() { + return i18next.t("code.melee"); + } + }, + "/Lotus/Weapons/Tenno/Pistol/LotusPistol": { + get name() { + return i18next.t("code.pistol"); + } + }, + "/Lotus/Weapons/Tenno/Rifle/LotusRifle": { + get name() { + return i18next.t("code.rifle"); + } + }, + "/Lotus/Weapons/Tenno/Shotgun/LotusShotgun": { + get name() { + return i18next.t("code.shotgun"); + } + }, + + // Modular weapons + "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary": { + get name() { + return i18next.t("code.kitgun"); + } + }, + "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam": { + get name() { + return i18next.t("code.kitgun"); + } + }, + "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryLauncher": { + get name() { + return i18next.t("code.kitgun"); + } + }, + "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun": { + get name() { + return i18next.t("code.kitgun"); + } + }, + "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimarySniper": { + get name() { + return i18next.t("code.kitgun"); + } + }, + "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary": { + get name() { + return i18next.t("code.kitgun"); + } + }, + "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam": { + get name() { + return i18next.t("code.kitgun"); + } + }, + "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun": { + get name() { + return i18next.t("code.kitgun"); + } + }, + "/Lotus/Weapons/Ostron/Melee/LotusModularWeapon": { + get name() { + return i18next.t("code.zaw"); + } + }, + "/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/OperatorTrainingAmpWeapon": { + get name() { + return i18next.t("code.moteAmp"); + } + }, + "/Lotus/Weapons/Sentients/OperatorAmplifiers/OperatorAmpWeapon": { + get name() { + return i18next.t("code.amp"); + } + }, + "/Lotus/Weapons/Operator/Pistols/DrifterPistol/DrifterPistolPlayerWeapon": { + get name() { + return i18next.t("code.sirocco"); + } + }, + "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit": { + get name() { + return i18next.t("code.kDrive"); + } + }, + // Missing in data sources - "/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser": { name: "Legendary Core" }, - "/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod": { name: "Traumatic Peculiar" } + "/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser": { + get name() { + return i18next.t("code.legendaryCore"); + } + }, + "/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod": { + get name() { + return i18next.t("code.traumaticPeculiar"); + } + } }; for (const [type, items] of Object.entries(data)) { if (type == "archonCrystalUpgrades") { @@ -173,7 +289,7 @@ function fetchItemList() { } else if (type != "badItems") { items.forEach(item => { if (item.uniqueName in data.badItems) { - item.name += " (Imposter)"; + item.name += " " + i18next.t("code.badItem"); } else if (item.uniqueName.substr(0, 18) != "/Lotus/Types/Game/") { const option = document.createElement("option"); option.setAttribute("data-key", item.uniqueName); @@ -272,7 +388,7 @@ function updateInventory() { } } }; - a.title = "Max Rank"; + a.title = i18next.t("code.maxRank"); a.innerHTML = ``; td.appendChild(a); } @@ -287,12 +403,12 @@ function updateInventory() { a.href = "#"; a.onclick = function (event) { event.preventDefault(); - const name = prompt("Enter new custom name:"); + const name = prompt(i18next.t("code.renamePrompt")); if (name !== null) { renameGear(category, item.ItemId.$oid, name); } }; - a.title = "Rename"; + a.title = i18next.t("code.rename"); a.innerHTML = ``; td.appendChild(a); } @@ -303,7 +419,7 @@ function updateInventory() { event.preventDefault(); disposeOfGear(category, item.ItemId.$oid); }; - a.title = "Remove"; + a.title = i18next.t("code.remove"); a.innerHTML = ``; td.appendChild(a); } @@ -327,11 +443,9 @@ function updateInventory() { const td = document.createElement("td"); td.textContent = itemMap[fingerprint.compat]?.name ?? fingerprint.compat; td.textContent += " " + RivenParser.parseRiven(rivenType, fingerprint, 1).name; - td.innerHTML += " ▲ " + fingerprint.buffs.length + ""; - td.innerHTML += - " ▼ " + fingerprint.curses.length + ""; - td.innerHTML += - " ⟳ " + parseInt(fingerprint.rerolls) + ""; + td.innerHTML += ` ▲ ${fingerprint.buffs.length}`; + td.innerHTML += ` ▼ ${fingerprint.curses.length}`; + td.innerHTML += ` ⟳ ${parseInt(fingerprint.rerolls)}`; tr.appendChild(td); } { @@ -349,7 +463,7 @@ function updateInventory() { }) ); a.target = "_blank"; - a.title = "View Stats"; + a.title = i18next.t("code.viewStats"); a.innerHTML = ``; td.appendChild(a); } @@ -360,7 +474,7 @@ function updateInventory() { event.preventDefault(); disposeOfGear("Upgrades", item.ItemId.$oid); }; - a.title = "Remove"; + a.title = i18next.t("code.remove"); a.innerHTML = ``; td.appendChild(a); } @@ -376,7 +490,7 @@ function updateInventory() { { const td = document.createElement("td"); td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType; - td.innerHTML += " ★ " + rank + "/" + maxRank + ""; + td.innerHTML += ` ★ 0/${maxRank}`; tr.appendChild(td); } { @@ -389,7 +503,7 @@ function updateInventory() { event.preventDefault(); setFingerprint(item.ItemType, item.ItemId, { lvl: maxRank }); }; - a.title = "Max Rank"; + a.title = i18next.t("code.maxRank"); a.innerHTML = ``; td.appendChild(a); } @@ -400,7 +514,7 @@ function updateInventory() { event.preventDefault(); disposeOfGear("Upgrades", item.ItemId.$oid); }; - a.title = "Remove"; + a.title = i18next.t("code.remove"); a.innerHTML = ``; td.appendChild(a); } @@ -415,9 +529,9 @@ function updateInventory() { { const td = document.createElement("td"); td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType; - td.innerHTML += " ★ 0/" + maxRank + ""; + td.innerHTML += ` ★ 0/${maxRank}`; if (item.ItemCount > 1) { - td.innerHTML += " 🗍 " + parseInt(item.ItemCount) + ""; + td.innerHTML += ` 🗍 ${parseInt(item.ItemCount)}`; } tr.appendChild(td); } @@ -431,7 +545,7 @@ function updateInventory() { event.preventDefault(); setFingerprint(item.ItemType, item.LastAdded, { lvl: maxRank }); }; - a.title = "Max Rank"; + a.title = i18next.t("code.maxRank"); a.innerHTML = ``; td.appendChild(a); } @@ -442,7 +556,7 @@ function updateInventory() { event.preventDefault(); disposeOfItems("Upgrades", item.ItemType, item.ItemCount); }; - a.title = "Remove"; + a.title = i18next.t("code.remove"); a.innerHTML = ``; td.appendChild(a); } @@ -491,7 +605,7 @@ function updateInventory() { event.preventDefault(); doPopArchonCrystalUpgrade(upgradeType); }; - a.title = "Remove"; + a.title = i18next.t("code.remove"); a.innerHTML = ``; td.appendChild(a); } @@ -570,10 +684,7 @@ function addMissingEquipment(categories) { } }); }); - if ( - requests.length != 0 && - window.confirm("Are you sure you want to add " + requests.length + " items to your account?") - ) { + if (requests.length !== 0 && window.confirm(i18next.t("code.addItemsConfirm", { count: requests.length }))) { dispatchAddItemsRequestsBatch(requests); } } @@ -627,7 +738,7 @@ function maxRankAllEquipment(categories) { return sendBatchGearExp(batchData); } - alert("No equipment to rank up."); + alert(i18next.t("code.noEquipmentToRankUp")); }); }); } @@ -743,7 +854,7 @@ function doAcquireMiscItems() { } ]) }).done(function () { - alert("Successfully added."); + alert(i18next.t("code.succAdded")); }); }); } @@ -938,13 +1049,9 @@ function doUnlockAllFocusSchools() { await unlockFocusSchool(upgradeType); } if (Object.keys(missingFocusUpgrades).length == 0) { - alert("All focus schools are already unlocked."); + alert(i18next.t("code.focusAllUnlocked")); } else { - alert( - "Unlocked " + - Object.keys(missingFocusUpgrades).length + - " new focus schools! An inventory update will be needed for the changes to be reflected in-game. Visiting the navigation should be the easiest way to trigger that." - ); + alert(i18next.t("code.focusUnlocked", { count: Object.keys(missingFocusUpgrades).length })); } }); }); @@ -994,10 +1101,7 @@ function doAddAllMods() { } modsAll = Array.from(modsAll); - if ( - modsAll.length != 0 && - window.confirm("Are you sure you want to add " + modsAll.length + " mods to your account?") - ) { + if (modsAll.length != 0 && window.confirm(i18next.t("code.addModsConfirm", { count: modsAll.length }))) { $.post({ url: "/custom/addItems?" + window.authz, contentType: "application/json", @@ -1071,7 +1175,7 @@ function doImport() { inventory: JSON.parse($("#import-inventory").val()) }) }).then(function () { - alert("Successfully imported."); + alert(i18next.t("code.succImport")); updateInventory(); }); }); diff --git a/static/webui/style.css b/static/webui/style.css index fbd3a65d..04379911 100644 --- a/static/webui/style.css +++ b/static/webui/style.css @@ -14,9 +14,7 @@ } } -body:not(.logged-in) .navbar-toggler, -body:not(.logged-in) .nav-item.dropdown, -body:not(.logged-in) #refresh-note { +body:not(.logged-in) .user-dropdown { display: none; } diff --git a/static/webui/translations/en.json b/static/webui/translations/en.json new file mode 100644 index 00000000..8a4cc569 --- /dev/null +++ b/static/webui/translations/en.json @@ -0,0 +1,129 @@ +{ + "general": { + "inventoryUpdateNote": "Note: Changes made here will only be reflected in-game when the game re-downloads your inventory. Visiting the navigation should be the easiest way to trigger that.", + "addButton": "Add", + "bulkActions": "Bulk Actions" + }, + "code": { + "nonValidAuthz": "Your credentials are no longer valid.", + "changeNameConfirm": "What would you like to change your account name to?", + "deleteAccountConfirm": "Are you sure you want to delete your account {{displayName}} ({{email}})? This action cannot be undone.", + "archgun": "Archgun", + "melee": "Melee", + "pistol": "Pistol", + "rifle": "Rifle", + "shotgun": "Shotgun", + "kitgun": "Kitgun", + "zaw": "Zaw", + "moteAmp": "Mote Amp", + "amp": "Amp", + "sirocco": "Sirocco", + "kDrive": "K-Drive", + "legendaryCore": "Legendary Core", + "traumaticPeculiar": "Traumatic Peculiar", + "badItem": "(Imposter)", + "maxRank": "Max Rank", + "rename": "Rename", + "renamePrompt": "Enter new custom name:", + "remove": "Remove", + "addItemsConfirm": "Are you sure you want to add {{count}} items to your account?", + "noEquipmentToRankUp": "No equipment to rank up.", + "succAdded": "Successfully added.", + "buffsNumber": "Number of buffs", + "cursesNumber": "Number of curses", + "rerollsNumber": "Number of rerolls", + "viewStats": "View Stats", + "rank": "Rank", + "count": "Count", + "focusAllUnlocked": "All focus schools are already unlocked.", + "focusUnlocked": "Unlocked {{count}} new focus schools! An inventory update will be needed for the changes to be reflected in-game. Visiting the navigation should be the easiest way to trigger that.", + "addModsConfirm": "Are you sure you want to add {{count}} mods to your account?", + "succImport": "Successfully imported." + }, + "login": { + "description": "Login using your OpenWF account credentials (same as in-game when connecting to this server).", + "emailLabel": "Email address", + "passwordLabel": "Password", + "loginButton": "Login" + }, + "navbar": { + "logout": "Logout", + "renameAccount": "Rename Account", + "deleteAccount": "Delete Account", + "inventory": "Inventory", + "mods": "Mods", + "cheats": "Cheats", + "import": "Import" + }, + "inventory": { + "addItems": "Add Items", + "suits": "Warframes", + "longGuns": "Primary Weapons", + "pistols": "Secondary Weapons", + "melee": "Melee Weapons", + "spaceSuits": "Archwings", + "spaceGuns": "Archwing Primary Weapons", + "spaceMelee": "Archwing Melee Weapons", + "mechSuits": "Necramechs", + "sentinels": "Sentinels", + "sentinelWeapons": "Sentinel Weapons", + "operatorAmps": "Amps", + "hoverboards": "K-Drives", + "bulkAddSuits": "Add Missing Warframes", + "bulkAddWeapons": "Add Missing Weapons", + "bulkAddSpaceSuits": "Add Missing Archwings", + "bulkAddSpaceWeapons": "Add Missing Archwing Weapons", + "bulkAddSentinels": "Add Missing Sentinels", + "bulkAddSentinelWeapons": "Add Missing Sentinel Weapons", + "bulkRankUpSuits": "Max Rank All Warframes", + "bulkRankUpWeapons": "Max Rank All Weapons", + "bulkRankUpSpaceSuits": "Max Rank All Archwings", + "bulkRankUpSpaceWeapons": "Max Rank All Archwing Weapons", + "bulkRankUpSentinels": "Max Rank All Sentinels", + "bulkRankUpSentinelWeapons": "Max Rank All Sentinel Weapons" + }, + "powersuit": { + "archonShardsLabel": "Archon Shard Slots", + "archonShardsDescription": "You can use these unlimited slots to apply a wide range of upgrades." + }, + "mods": { + "addRiven": "Add Riven", + "fingerprint": "Fingerprint", + "fingerprintHelp": "Need help with the fingerprint?", + "rivens": "Rivens", + "mods": "Mods", + "bulkAddMods": "Add Missing Mods" + }, + "cheats": { + "administratorRequirement": "You must be an administrator to use this feature. To become an administrator, add \"\" to administratorNames in the config.json.", + "skipTutorial": "Skip Tutorial", + "skipAllDialogue": "Skip All Dialogue", + "unlockAllScans": "Unlock All Scans", + "unlockAllMissions": "Unlock All Missions", + "unlockAllQuests": "Unlock All Quests", + "completeAllQuests": "Complete All Quests", + "infiniteCredits": "Infinite Credits", + "infinitePlatinum": "Infinite Platinum", + "infiniteEndo": "Infinite Endo", + "infiniteRegalAya": "Infinite Regal Aya", + "unlockAllShipFeatures": "Unlock All Ship Features", + "unlockAllShipDecorations": "Unlock All Ship Decorations", + "unlockAllFlavourItems": "Unlock All Flavor Items", + "unlockAllSkins": "Unlock All Skins", + "unlockAllCapturaScenes": "Unlock All Captura Scenes", + "universalPolarityEverywhere": "Universal Polarity Everywhere", + "unlockDoubleCapacityPotatoesEverywhere": "Potatoes Everywhere", + "unlockExilusEverywhere": "Exilus Adapters Everywhere", + "unlockArcanesEverywhere": "Arcane Adapters Everywhere", + "noDailyStandingLimits": "No Daily Standing Limits", + "spoofMasteryRank": "Spoofed Mastery Rank (-1 to disable)", + "saveSettings": "Save Settings", + "account": "Account", + "unlockAllFocusSchools": "Unlock All Focus Schools", + "helminthUnlockAll": "Fully Level Up Helminth" + }, + "import": { + "importNote": "You can provide a full or partial inventory response (client respresentation) here. All fields that are supported by the importer will be overwritten in your account.", + "submit": "Submit" + } +} diff --git a/static/webui/translations/ru.json b/static/webui/translations/ru.json new file mode 100644 index 00000000..bd2bc769 --- /dev/null +++ b/static/webui/translations/ru.json @@ -0,0 +1,129 @@ +{ + "general": { + "inventoryUpdateNote": "Примечание: изменения, внесенные здесь, отобразятся в игре только после повторной загрузки вашего инвентаря. Посещение навигации — самый простой способ этого добиться.", + "addButton": "Добавить", + "bulkActions": "Массовые действия" + }, + "code": { + "nonValidAuthz": "Ваши данные больше не действительны.", + "changeNameConfirm": "Какое имя вы хотите установить для своей учетной записи?", + "deleteAccountConfirm": "Вы уверены, что хотите удалить аккаунт {{displayName}} ({{email}})? Это действие нельзя отменить.", + "archgun": "Арч-Пушка", + "melee": "Ближний бой", + "pistol": "Пистолет", + "rifle": "Винтовка", + "shotgun": "Дробовик", + "kitgun": "Китган", + "zaw": "Зо", + "moteAmp": "Пылинка", + "amp": "Усилитель", + "sirocco": "Сирокко", + "kDrive": "К-Драйв", + "legendaryCore": "Легендарное ядро", + "traumaticPeculiar": "Травмирующая Странность", + "badItem": "(Самозванец)", + "maxRank": "Максимальный ранг", + "rename": "Переименовать", + "renamePrompt": "Введите новое имя:", + "remove": "Удалить", + "addItemsConfirm": "Вы уверены, что хотите добавить {{count}} предметов на ваш аккаунт?", + "noEquipmentToRankUp": "Нет снаряжения для повышения ранга.", + "succAdded": "Успешно добавлено.", + "buffsNumber": "Количество усилений", + "cursesNumber": "Количество проклятий", + "rerollsNumber": "Количество циклов", + "viewStats": "Просмотр характеристики", + "rank": "Ранг", + "count": "Количество", + "focusAllUnlocked": "Все школы фокуса уже разблокированы.", + "focusUnlocked": "Разблокировано {{count}} новых школ фокуса! Для отображения изменений в игре потребуется обновление инвентаря. Посещение навигации — самый простой способ этого добиться.", + "addModsConfirm": "Вы уверены, что хотите добавить {{count}} модов на ваш аккаунт?", + "succImport": "Успешно импортировано." + }, + "login": { + "description": "Войдите, используя учетные данные OpenWF (те же, что и в игре при подключении к этому серверу).", + "emailLabel": "Адрес электронной почты", + "passwordLabel": "Пароль", + "loginButton": "Войти" + }, + "navbar": { + "logout": "Выйти", + "renameAccount": "Переименовать аккаунт", + "deleteAccount": "Удалить аккаунт", + "inventory": "Инвентарь", + "mods": "Моды", + "cheats": "Читы", + "import": "Импорт" + }, + "inventory": { + "addItems": "Добавить предметы", + "suits": "Варфреймы", + "longGuns": "Основное оружие", + "pistols": "Вторичное оружие", + "melee": "Оружие ближнего боя", + "spaceSuits": "Арчвинги", + "spaceGuns": "Оружие арчвинга", + "spaceMelee": "Оружие ближнего боя арчвинга", + "mechSuits": "Некрамехи", + "sentinels": "Стражи", + "sentinelWeapons": "Оружие стражей", + "operatorAmps": "Усилители", + "hoverboards": "К-Драйвы", + "bulkAddSuits": "Добавить отсутствующие варфреймы", + "bulkAddWeapons": "Добавить отсутствующее оружие", + "bulkAddSpaceSuits": "Добавить отсутствующие арчвинги", + "bulkAddSpaceWeapons": "Добавить отсутствующее оружие арчвингов", + "bulkAddSentinels": "Добавить отсутствующих стражей", + "bulkAddSentinelWeapons": "Добавить отсутствующее оружие стражей", + "bulkRankUpSuits": "Максимальный ранг всех варфреймов", + "bulkRankUpWeapons": "Максимальный ранг всего оружия", + "bulkRankUpSpaceSuits": "Максимальный ранг всех арчвингов", + "bulkRankUpSpaceWeapons": "Максимальный ранг всего оружия арчвингов", + "bulkRankUpSentinels": "Максимальный ранг всех стражей", + "bulkRankUpSentinelWeapons": "Максимальный ранг всего оружия стражей" + }, + "powersuit": { + "archonShardsLabel": "Ячейки осколков архонта", + "archonShardsDescription": "Вы можете использовать эти неограниченные ячейки для установки множества улучшений." + }, + "mods": { + "addRiven": "Добавить Мод Разлома", + "fingerprint": "Отпечаток", + "fingerprintHelp": "Нужна помощь с отпечатком?", + "rivens": "Моды Разлома", + "mods": "Моды", + "bulkAddMods": "Добавить отсутствующие моды" + }, + "cheats": { + "administratorRequirement": "Вы должны быть администратором для использования этой функции. Чтобы стать администратором, добавьте \"\" в administratorNames в config.json.", + "skipTutorial": "Пропустить обучение", + "skipAllDialogue": "Пропустить все диалоги", + "unlockAllScans": "Разблокировать все сканирования", + "unlockAllMissions": "Разблокировать все миссии", + "unlockAllQuests": "Разблокировать все квесты", + "completeAllQuests": "Завершить все квесты", + "infiniteCredits": "Бесконечные кредиты", + "infinitePlatinum": "Бесконечный платина", + "infiniteEndo": "Бесконечное эндо", + "infiniteRegalAya": "Бесконечная Королевская Айя", + "unlockAllShipFeatures": "Разблокировать все функции корабля", + "unlockAllShipDecorations": "Разблокировать все украшения корабля", + "unlockAllFlavourItems": "Разблокировать все уникальные предметы", + "unlockAllSkins": "Разблокировать все скины", + "unlockAllCapturaScenes": "Разблокировать все сцены Каптуры", + "universalPolarityEverywhere": "Универсальная полярность везде", + "unlockDoubleCapacityPotatoesEverywhere": "Катализаторы везде", + "unlockExilusEverywhere": "Адаптеры Эксилус везде", + "unlockArcanesEverywhere": "Адаптеры для мистификаторов везде", + "noDailyStandingLimits": "Без ежедневных ограничений репутации", + "spoofMasteryRank": "Подделанный ранг мастерства (-1 для отключения)", + "saveSettings": "Сохранить настройки", + "account": "Аккаунт", + "unlockAllFocusSchools": "Разблокировать все школы фокуса", + "helminthUnlockAll": "Полностью улучшить Гельминта" + }, + "import": { + "importNote": "Вы можете загрузить полный или частичный ответ инвентаря (клиентское представление) здесь. Все поддерживаемые поля будут перезаписаны в вашем аккаунте.", + "submit": "Отправить" + } +}