Localize HeliosLauncher UI using lang files (#301)
* First step to use Language .json file in ejs * i18n for landing.ejs * i18n for login.ejs * i18n for loginOptions.ejs * i18n for overlay.ejs * i18n for settings.ejs * i18n for waiting.ejs * i18n for welcome.ejs * langloader.js placeholder support * i18n for landing.js * i18n for login.js * i18n for overlay.js * i18n for settings.js * i18n for uibinder.js * i18n for uicore.js * remove html language replacement * use toml for i18n * Fix mojang/microsoft status icon is undefined * cascadable langloader * separate lang file for customization * move some placeholder text to _placeholder.toml * Update * Reduce package lock diff. * Remove another placeholder. * Checkbox does not require translation. * Icons don't need translation. * Leave placeholders inline. * Fix translation for news pages. * Remove more unneeded translations. --------- Co-authored-by: Daniel Scalzi <d_scalzi@yahoo.com>
This commit is contained in:
@@ -59,8 +59,8 @@ function bindFileSelectors(){
|
||||
|
||||
if(isJavaExecSel && process.platform === 'win32') {
|
||||
options.filters = [
|
||||
{ name: 'Executables', extensions: ['exe'] },
|
||||
{ name: 'All Files', extensions: ['*'] }
|
||||
{ name: Lang.queryJS('settings.fileSelectors.executables'), extensions: ['exe'] },
|
||||
{ name: Lang.queryJS('settings.fileSelectors.allFiles'), extensions: ['*'] }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -374,9 +374,9 @@ ipcRenderer.on(MSFT_OPCODE.REPLY_LOGIN, (_, ...arguments_) => {
|
||||
|
||||
// Unexpected error.
|
||||
setOverlayContent(
|
||||
'Something Went Wrong',
|
||||
'Microsoft authentication failed. Please try again.',
|
||||
'OK'
|
||||
Lang.queryJS('settings.msftLogin.errorTitle'),
|
||||
Lang.queryJS('settings.msftLogin.errorMessage'),
|
||||
Lang.queryJS('settings.msftLogin.okButton')
|
||||
)
|
||||
setOverlayHandler(() => {
|
||||
toggleOverlay(false)
|
||||
@@ -401,7 +401,7 @@ ipcRenderer.on(MSFT_OPCODE.REPLY_LOGIN, (_, ...arguments_) => {
|
||||
setOverlayContent(
|
||||
error,
|
||||
errorDesc,
|
||||
'OK'
|
||||
Lang.queryJS('settings.msftLogin.okButton')
|
||||
)
|
||||
setOverlayHandler(() => {
|
||||
toggleOverlay(false)
|
||||
@@ -429,10 +429,7 @@ ipcRenderer.on(MSFT_OPCODE.REPLY_LOGIN, (_, ...arguments_) => {
|
||||
} else {
|
||||
// Uh oh.
|
||||
msftLoginLogger.error('Unhandled error during login.', displayableError)
|
||||
actualDisplayableError = {
|
||||
title: 'Unknown Error During Login',
|
||||
desc: 'An unknown error has occurred. Please see the console for details.'
|
||||
}
|
||||
actualDisplayableError = Lang.queryJS('login.error.unknown')
|
||||
}
|
||||
|
||||
switchView(getCurrentView(), viewOnClose, 500, 500, () => {
|
||||
@@ -461,11 +458,11 @@ function bindAuthAccountSelect(){
|
||||
for(let i=0; i<selectBtns.length; i++){
|
||||
if(selectBtns[i].hasAttribute('selected')){
|
||||
selectBtns[i].removeAttribute('selected')
|
||||
selectBtns[i].innerHTML = 'Select Account'
|
||||
selectBtns[i].innerHTML = Lang.queryJS('settings.authAccountSelect.selectButton')
|
||||
}
|
||||
}
|
||||
val.setAttribute('selected', '')
|
||||
val.innerHTML = 'Selected Account ✔'
|
||||
val.innerHTML = Lang.queryJS('settings.authAccountSelect.selectedButton')
|
||||
setSelectedAccount(val.closest('.settingsAuthAccount').getAttribute('uuid'))
|
||||
}
|
||||
})
|
||||
@@ -483,10 +480,10 @@ function bindAuthAccountLogOut(){
|
||||
if(Object.keys(ConfigManager.getAuthAccounts()).length === 1){
|
||||
isLastAccount = true
|
||||
setOverlayContent(
|
||||
'Warning<br>This is Your Last Account',
|
||||
'In order to use the launcher you must be logged into at least one account. You will need to login again after.<br><br>Are you sure you want to log out?',
|
||||
'I\'m Sure',
|
||||
'Cancel'
|
||||
Lang.queryJS('settings.authAccountLogout.lastAccountWarningTitle'),
|
||||
Lang.queryJS('settings.authAccountLogout.lastAccountWarningMessage'),
|
||||
Lang.queryJS('settings.authAccountLogout.confirmButton'),
|
||||
Lang.queryJS('settings.authAccountLogout.cancelButton')
|
||||
)
|
||||
setOverlayHandler(() => {
|
||||
processLogOut(val, isLastAccount)
|
||||
@@ -555,9 +552,9 @@ ipcRenderer.on(MSFT_OPCODE.REPLY_LOGOUT, (_, ...arguments_) => {
|
||||
|
||||
// Unexpected error.
|
||||
setOverlayContent(
|
||||
'Something Went Wrong',
|
||||
'Microsoft logout failed. Please try again.',
|
||||
'OK'
|
||||
Lang.queryJS('settings.msftLogout.errorTitle'),
|
||||
Lang.queryJS('settings.msftLogout.errorMessage'),
|
||||
Lang.queryJS('settings.msftLogout.okButton')
|
||||
)
|
||||
setOverlayHandler(() => {
|
||||
toggleOverlay(false)
|
||||
@@ -611,12 +608,12 @@ function refreshAuthAccountSelected(uuid){
|
||||
const selBtn = val.getElementsByClassName('settingsAuthAccountSelect')[0]
|
||||
if(uuid === val.getAttribute('uuid')){
|
||||
selBtn.setAttribute('selected', '')
|
||||
selBtn.innerHTML = 'Selected Account ✔'
|
||||
selBtn.innerHTML = Lang.queryJS('settings.authAccountSelect.selectedButton')
|
||||
} else {
|
||||
if(selBtn.hasAttribute('selected')){
|
||||
selBtn.removeAttribute('selected')
|
||||
}
|
||||
selBtn.innerHTML = 'Select Account'
|
||||
selBtn.innerHTML = Lang.queryJS('settings.authAccountSelect.selectButton')
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -648,18 +645,18 @@ function populateAuthAccounts(){
|
||||
<div class="settingsAuthAccountRight">
|
||||
<div class="settingsAuthAccountDetails">
|
||||
<div class="settingsAuthAccountDetailPane">
|
||||
<div class="settingsAuthAccountDetailTitle">Username</div>
|
||||
<div class="settingsAuthAccountDetailTitle">${Lang.queryJS('settings.authAccountPopulate.username')}</div>
|
||||
<div class="settingsAuthAccountDetailValue">${acc.displayName}</div>
|
||||
</div>
|
||||
<div class="settingsAuthAccountDetailPane">
|
||||
<div class="settingsAuthAccountDetailTitle">UUID</div>
|
||||
<div class="settingsAuthAccountDetailTitle">${Lang.queryJS('settings.authAccountPopulate.uuid')}</div>
|
||||
<div class="settingsAuthAccountDetailValue">${acc.uuid}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settingsAuthAccountActions">
|
||||
<button class="settingsAuthAccountSelect" ${selectedUUID === acc.uuid ? 'selected>Selected Account ✔' : '>Select Account'}</button>
|
||||
<button class="settingsAuthAccountSelect" ${selectedUUID === acc.uuid ? 'selected>' + Lang.queryJS('settings.authAccountPopulate.selectedAccount') : '>' + Lang.queryJS('settings.authAccountPopulate.selectAccount')}</button>
|
||||
<div class="settingsAuthAccountWrapper">
|
||||
<button class="settingsAuthAccountLogOut">Log Out</button>
|
||||
<button class="settingsAuthAccountLogOut">${Lang.queryJS('settings.authAccountPopulate.logout')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -873,7 +870,7 @@ async function resolveDropinModsForUI(){
|
||||
<div class="settingsModDetails">
|
||||
<span class="settingsModName">${dropin.name}</span>
|
||||
<div class="settingsDropinRemoveWrapper">
|
||||
<button class="settingsDropinRemoveButton" remmod="${dropin.fullName}">Remove</button>
|
||||
<button class="settingsDropinRemoveButton" remmod="${dropin.fullName}">${Lang.queryJS('settings.dropinMods.removeButton')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -901,9 +898,9 @@ function bindDropinModsRemoveButton(){
|
||||
document.getElementById(fullName).remove()
|
||||
} else {
|
||||
setOverlayContent(
|
||||
`Failed to Delete<br>Drop-in Mod ${fullName}`,
|
||||
'Make sure the file is not in use and try again.',
|
||||
'Okay'
|
||||
Lang.queryJS('settings.dropinMods.deleteFailedTitle', { fullName }),
|
||||
Lang.queryJS('settings.dropinMods.deleteFailedMessage'),
|
||||
Lang.queryJS('settings.okButton')
|
||||
)
|
||||
setOverlayHandler(null)
|
||||
toggleOverlay(true)
|
||||
@@ -956,9 +953,9 @@ function saveDropinModConfiguration(){
|
||||
DropinModUtil.toggleDropinMod(CACHE_SETTINGS_MODS_DIR, dropin.fullName, dropinUIEnabled).catch(err => {
|
||||
if(!isOverlayVisible()){
|
||||
setOverlayContent(
|
||||
'Failed to Toggle<br>One or More Drop-in Mods',
|
||||
Lang.queryJS('settings.dropinMods.failedToggleTitle'),
|
||||
err.message,
|
||||
'Okay'
|
||||
Lang.queryJS('settings.okButton')
|
||||
)
|
||||
setOverlayHandler(null)
|
||||
toggleOverlay(true)
|
||||
@@ -1093,7 +1090,7 @@ async function loadSelectedServerOnModsTab(){
|
||||
<path class="cls-1" d="M100.93,65.54C89,62,68.18,55.65,63.54,52.13c2.7-5.23,18.8-19.2,28-27.55C81.36,31.74,63.74,43.87,58.09,45.3c-2.41-5.37-3.61-26.52-4.37-39-.77,12.46-2,33.64-4.36,39-5.7-1.46-23.3-13.57-33.49-20.72,9.26,8.37,25.39,22.36,28,27.55C39.21,55.68,18.47,62,6.52,65.55c12.32-2,33.63-6.06,39.34-4.9-.16,5.87-8.41,26.16-13.11,37.69,6.1-10.89,16.52-30.16,21-33.9,4.5,3.79,14.93,23.09,21,34C70,86.84,61.73,66.48,61.59,60.65,67.36,59.49,88.64,63.52,100.93,65.54Z"/>
|
||||
<circle class="cls-2" cx="53.73" cy="53.9" r="38"/>
|
||||
</svg>
|
||||
<span class="serverListingStarTooltip">Main Server</span>
|
||||
<span class="serverListingStarTooltip">${Lang.queryJS('settings.serverListing.mainServer')}</span>
|
||||
</div>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1344,19 +1341,19 @@ async function populateJavaExecDetails(execPath){
|
||||
const details = await validateSelectedJvm(ensureJavaDirIsRoot(execPath), server.effectiveJavaOptions.supported)
|
||||
|
||||
if(details != null) {
|
||||
settingsJavaExecDetails.innerHTML = `Selected: Java ${details.semverStr} (${details.vendor})`
|
||||
settingsJavaExecDetails.innerHTML = Lang.queryJS('settings.java.selectedJava', { version: details.semverStr, vendor: details.vendor })
|
||||
} else {
|
||||
settingsJavaExecDetails.innerHTML = 'Invalid Selection'
|
||||
settingsJavaExecDetails.innerHTML = Lang.queryJS('settings.java.invalidSelection')
|
||||
}
|
||||
}
|
||||
|
||||
function populateJavaReqDesc(server) {
|
||||
settingsJavaReqDesc.innerHTML = `Requires Java ${server.effectiveJavaOptions.suggestedMajor} x64.`
|
||||
settingsJavaReqDesc.innerHTML = Lang.queryJS('settings.java.requiresJava', { major: server.effectiveJavaOptions.suggestedMajor })
|
||||
}
|
||||
|
||||
function populateJvmOptsLink(server) {
|
||||
const major = server.effectiveJavaOptions.suggestedMajor
|
||||
settingsJvmOptsLink.innerHTML = `Available Options for Java ${major} (HotSpot VM)`
|
||||
settingsJvmOptsLink.innerHTML = Lang.queryJS('settings.java.availableOptions', { major: major })
|
||||
if(major >= 12) {
|
||||
settingsJvmOptsLink.href = `https://docs.oracle.com/en/java/javase/${major}/docs/specs/man/java.html#extra-options-for-java`
|
||||
}
|
||||
@@ -1433,11 +1430,11 @@ function isPrerelease(version){
|
||||
function populateVersionInformation(version, valueElement, titleElement, checkElement){
|
||||
valueElement.innerHTML = version
|
||||
if(isPrerelease(version)){
|
||||
titleElement.innerHTML = 'Pre-release'
|
||||
titleElement.innerHTML = Lang.queryJS('settings.about.preReleaseTitle')
|
||||
titleElement.style.color = '#ff886d'
|
||||
checkElement.style.background = '#ff886d'
|
||||
} else {
|
||||
titleElement.innerHTML = 'Stable Release'
|
||||
titleElement.innerHTML = Lang.queryJS('settings.about.stableReleaseTitle')
|
||||
titleElement.style.color = null
|
||||
checkElement.style.background = null
|
||||
}
|
||||
@@ -1476,7 +1473,7 @@ function populateReleaseNotes(){
|
||||
},
|
||||
timeout: 2500
|
||||
}).catch(err => {
|
||||
settingsAboutChangelogText.innerHTML = 'Failed to load release notes.'
|
||||
settingsAboutChangelogText.innerHTML = Lang.queryJS('settings.about.releaseNotesFailed')
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1524,27 +1521,27 @@ function settingsUpdateButtonStatus(text, disabled = false, handler = null){
|
||||
*/
|
||||
function populateSettingsUpdateInformation(data){
|
||||
if(data != null){
|
||||
settingsUpdateTitle.innerHTML = `New ${isPrerelease(data.version) ? 'Pre-release' : 'Release'} Available`
|
||||
settingsUpdateTitle.innerHTML = isPrerelease(data.version) ? Lang.queryJS('settings.updates.newPreReleaseTitle') : Lang.queryJS('settings.updates.newReleaseTitle')
|
||||
settingsUpdateChangelogCont.style.display = null
|
||||
settingsUpdateChangelogTitle.innerHTML = data.releaseName
|
||||
settingsUpdateChangelogText.innerHTML = data.releaseNotes
|
||||
populateVersionInformation(data.version, settingsUpdateVersionValue, settingsUpdateVersionTitle, settingsUpdateVersionCheck)
|
||||
|
||||
if(process.platform === 'darwin'){
|
||||
settingsUpdateButtonStatus('Download from GitHub<span style="font-size: 10px;color: gray;text-shadow: none !important;">Close the launcher and run the dmg to update.</span>', false, () => {
|
||||
settingsUpdateButtonStatus(Lang.queryJS('settings.updates.downloadButton'), false, () => {
|
||||
shell.openExternal(data.darwindownload)
|
||||
})
|
||||
} else {
|
||||
settingsUpdateButtonStatus('Downloading..', true)
|
||||
settingsUpdateButtonStatus(Lang.queryJS('settings.updates.downloadingButton'), true)
|
||||
}
|
||||
} else {
|
||||
settingsUpdateTitle.innerHTML = 'You Are Running the Latest Version'
|
||||
settingsUpdateTitle.innerHTML = Lang.queryJS('settings.updates.latestVersionTitle')
|
||||
settingsUpdateChangelogCont.style.display = 'none'
|
||||
populateVersionInformation(remote.app.getVersion(), settingsUpdateVersionValue, settingsUpdateVersionTitle, settingsUpdateVersionCheck)
|
||||
settingsUpdateButtonStatus('Check for Updates', false, () => {
|
||||
settingsUpdateButtonStatus(Lang.queryJS('settings.updates.checkForUpdatesButton'), false, () => {
|
||||
if(!isDev){
|
||||
ipcRenderer.send('autoUpdateAction', 'checkForUpdate')
|
||||
settingsUpdateButtonStatus('Checking for Updates..', true)
|
||||
settingsUpdateButtonStatus(Lang.queryJS('settings.updates.checkingForUpdatesButton'), true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user