Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: add i18n support; add Chinese language; add settings to change the language. #1332

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ export default class App extends mixins(UtilityMixin) {
// Load settings using the default game before the actual game is selected.
const settings: ManagerSettings = await this.$store.dispatch('resetActiveGame');

// set display language from settings
if(typeof settings.getContext().global.displayLanguage === 'undefined'){
settings.setDisplayLanguage(this.$i18n.locale);
}else{
this.$i18n.locale = settings.getContext().global.displayLanguage;
}

this.hookThunderstoreModListRefresh();
await this.checkCdnConnection();

Expand Down
12 changes: 7 additions & 5 deletions src/boot/i18n.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import messages from '../i18n';
import Quasar from 'quasar';

Vue.use(VueI18n);

const i18n = new VueI18n({
locale: 'en-us',
fallbackLocale: 'en-us',
messages,
// Detecting Locale
locale: Quasar.lang.getLocale(),
fallbackLocale: 'en-us',
messages
});

export default ({ app }: any) => {
// Set i18n instance on app
app.i18n = i18n;
// Set i18n instance on app
app.i18n = i18n;
};

export { i18n };
6 changes: 3 additions & 3 deletions src/components/ExpandableCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
<slot name='other-icons'></slot>
<!-- Allow movement of mod order -->
<a v-if='showSort' class='card-header-icon handle'>
<i class="fas fa-grip-vertical" v-tooltip.left="'Drag to reorder'"></i>
<i class="fas fa-grip-vertical" v-tooltip.left="$t(`card.tip1`)"></i>
</a>
<a class='card-header-icon'>
<span class='icon'>
<i class='fas fa-angle-right' aria-hidden='true' v-if='!visible' v-tooltip.left="'Expand'"></i>
<i class='fas fa-angle-down' aria-hidden='true' v-if='visible' v-tooltip.left="'Collapse'"></i>
<i class='fas fa-angle-right' aria-hidden='true' v-if='!visible' v-tooltip.left="$t(`card.tip2`)"></i>
<i class='fas fa-angle-down' aria-hidden='true' v-if='visible' v-tooltip.left="$t(`card.tip3`)"></i>
</span>
</a>
</header>
Expand Down
26 changes: 8 additions & 18 deletions src/components/SettingsLoader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,35 @@
<div v-if="phase > PHASES.ERROR_STATES" class="modal z-top is-active">
<div class="modal-content">
<div class="notification is-danger">
<h3 class="title">Error</h3>
<h3 class="title">{{ $t(`settingsLoader.title`) }}</h3>
<h5 class="title is-5">{{error && error.name}}</h5>
<p>{{error && error.message}}</p>
<br />
<h5 class="title is-5">Suggestion</h5>
<h5 class="title is-5">{{ $t(`settingsLoader.suggestion`) }}</h5>

<p v-if="phase === PHASES.GAME_FAILED">
This is a problem with the mod manager itself.
If there's a newer version of the manager
available, try installing it.
{{ $t(`settingsLoader.gameFailed`) }}
</p>

<div v-else-if="phase === PHASES.SETTINGS_FAILED">
<p>
Loading of local user settings failed. You
can use the button below to reset the
settings, but note that all settings for all
games will be lost and this can't be undone.
{{ $t(`settingsLoader.settingsFailed`) }}
</p>
<br />
<button @click="resetSettings" class="button is-white">
Reset settings
{{ $t(`settingsLoader.reset`) }}
</button>
</div>

<p v-else-if="phase === PHASES.RESET_FAILED">
Resetting of the settings failed. You can still
try to reset the settings manually by following
these
{{ $t(`settingsLoader.resetFailed`) }}
<a @click="openLink('https://github.com/ebkr/r2modmanPlus/wiki/Error:-White-or-blank-game-select-screen-on-startup#corrupted-settings-on-update')">
instructions.
{{ $t(`settingsLoader.instructions`) }}
</a>
</p>

<p v-else-if="phase === PHASES.RETRY_FAILED">
Locally stored settings were reset, but that
didn't solve the issue with loading the
settings. If there's a newer version of the
manager available, try installing it.
{{ $t(`settingsLoader.retryFailed`) }}
</p>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/components/buttons/DonateButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<Link :url="mod.getDonationLink()"
:target="'external'"
class="card-footer-item"
v-tooltip.left="{content: 'Donate to the mod author', distance: 0}">
v-tooltip.left="{content: $t('buttons.tip'), distance: 0}">
<i class='fas fa-heart margin-right margin-right--half-width'></i>
Donate
{{ $t('buttons.donate') }}
</Link>
</template>

Expand Down
14 changes: 7 additions & 7 deletions src/components/config-components/ConfigEditLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
<div>
<Hero
:title="configFile.getName()"
subtitle="Editing config file"
:subtitle="$t('config.edit.subtitle')"
hero-type="is-info"
/>
<br/>
<div class="sticky-top sticky-top--buttons margin-right">
<button class="button is-info margin-right margin-right--half-width" @click="save">Save</button>
<button class="button is-danger" @click="cancel">Cancel</button>
<button class="button is-info margin-right margin-right--half-width" @click="save">{{ $t('config.edit.save') }}</button>
<button class="button is-danger" @click="cancel">{{ $t('config.edit.cancel') }}</button>
</div>
<div v-if="configFile.getPath().toLowerCase().endsWith('.cfg')" class="margin-right non-selectable">
<h3 class='subtitle is-3'>Sections</h3>
<h3 class='subtitle is-3'>{{ $t('config.edit.sections') }}</h3>
<ul>
<li v-for="(value, key) in dumpedConfigVariables" :key="`${key}-${value.toString()}-tab`">
<a :href="`#${key}`">{{ key }}</a>
Expand All @@ -28,10 +28,10 @@
<p class="subtitle is-italic is-bold is-6 is-marginless">
<template v-if="getCommentDisplay(line.comments).split('\n').length > 4">
<span class="pre selectable" v-if="!line.commentsExpanded">{{getCommentDisplayShort(line.comments)}}</span>
<span class="pre selectable" v-else="!line.commentsExpanded">{{getCommentDisplay(line.comments)}}</span>
<span class="pre selectable" v-else>{{getCommentDisplay(line.comments)}}</span>
<a @click="toggleEntryExpansion(key, variable)">
<span v-if="!line.commentsExpanded">Show more</span>
<span v-else>Show less</span>
<span v-if="!line.commentsExpanded">{{ $t('config.edit.more') }}</span>
<span v-else>{{ $t('config.edit.less') }}</span>
</a>
</template>
<span class="pre" v-else>{{getCommentDisplay(line.comments)}}</span>
Expand Down
30 changes: 15 additions & 15 deletions src/components/config-components/ConfigSelectionLayout.vue
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
<template>
<div>
<Hero
title="Config editor"
subtitle="Select a configuration file to edit"
:title="$t('config.selection.title')"
:subtitle="$t('config.selection.subtitle')"
hero-type="is-info"
/>
<div class="notification is-warning is-square">
<div class="container">
<p>
Configuration files are generated after launching the game, with the mod installed, at least once.
{{ $t('config.selection.notification') }}
</p>
</div>
</div>
<div class='is-shadowless'>
<div class='no-padding-left card-header-title'>

<div class="input-group input-group--flex margin-right">
<label for="local-search" class="non-selectable">Search</label>
<input id="local-search" v-model='filterText' class="input margin-right" type="text" placeholder="Search for config files"/>
<label for="local-search" class="non-selectable">{{ $t('config.selection.search') }}</label>
<input id="local-search" v-model='filterText' class="input margin-right" type="text" :placeholder="$t('config.selection.searchPH')"/>
</div>

<div class="input-group margin-right">
<label for="config-sort-order" class="non-selectable">Sort</label>
<label for="config-sort-order" class="non-selectable">{{ $t('config.selection.sort') }}</label>
<select id="config-sort-order" class="select select--content-spacing margin-right margin-right--half-width" v-model="sortOrder">
<option v-for="(key, index) in getSortOrderOptions()" :key="`${index}-deprecated-position-option`">
{{key}}
<option v-for="(key, index) in getSortOrderOptions()" :key="`${index}-deprecated-position-option`" :value="key">
{{ $t(`config.selection.SortConfigFile.${key}`) }}
</option>
</select>
<select id="config-sort-direction" class="select select--content-spacing" v-model="sortDirection">
<option v-for="(key, index) in getSortDirectionOptions()" :key="`${index}-deprecated-position-option`">
{{key}}
<option v-for="(key, index) in getSortDirectionOptions()" :key="`${index}-deprecated-position-option`" :value="key">
{{ $t(`config.selection.SortDirection.${key}`) }}
</option>
</select>
</div>
Expand All @@ -44,9 +44,9 @@
<template v-slot:title>
<span>{{file.getName()}}</span>
</template>
<a class='card-footer-item' @click="editConfig(file)">Edit Config</a>
<a class='card-footer-item' @click="openConfig(file)">Open File</a>
<a class='card-footer-item' @click="deleteConfig(file)">Delete</a>
<a class='card-footer-item' @click="editConfig(file)">{{ $t('config.selection.edit') }}</a>
<a class='card-footer-item' @click="openConfig(file)">{{ $t('config.selection.open') }}</a>
<a class='card-footer-item' @click="deleteConfig(file)">{{ $t('config.selection.delete') }}</a>
</ExpandableCard>
</div>
</div>
Expand Down Expand Up @@ -146,8 +146,8 @@ import ProfileModList from '../../r2mm/mods/ProfileModList';
} catch (e) {
this.$store.commit("error/handleError", R2Error.fromThrownValue(
e,
"Failed to delete config file",
`Try running ${ManagerInformation.APP_NAME} as an administrator.`
this.$t('config.selection.failed'),
this.$t('config.selection.try',{ appName: ManagerInformation.APP_NAME })
));
}
}
Expand Down
50 changes: 25 additions & 25 deletions src/components/importing/LocalFileImportModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@
<div>
<Modal :show-close="true" @close-modal="emitClose" :open="visible">
<template slot="title">
<p class='card-header-title'>Import mod from file</p>
<p class='card-header-title'>{{ $t('importing.title') }}</p>
</template>
<template slot="footer" v-if="fileToImport === null">
<button class="button is-info" @click="selectFile">Select file</button>
<button class="button is-info" @click="selectFile">{{ $t('importing.select') }}</button>
</template>
<template slot="footer" v-else-if="fileToImport !== null">
<button class="button is-info" @click="importFile">Import local mod</button>
<button class="button is-info" @click="importFile">{{ $t('importing.import') }}</button>
</template>

<template slot="body" v-if="fileToImport === null">
<template v-if="!waitingForSelection">
<p>Please select a zip or DLL to be imported.</p>
<p>Zip files that contain a manifest file will have the some information pre-filled. If a manifest is not available, this will have to be entered manually.</p>
<p>{{ $t('importing.tip') }}</p>
<p>{{ $t('importing.desc') }}</p>
</template>
<template v-else>
<p>Waiting for file. This may take a minute.</p>
<p>{{ $t('importing.waiting') }}</p>
</template>
</template>

Expand All @@ -26,33 +26,33 @@
<p>{{ validationMessage }}</p>
</div>
<div class="input-group input-group--flex margin-right">
<label for="mod-name" class="non-selectable">Mod name</label>
<input id="mod-name" ref="mod-name" class="input margin-right" type="text" v-model="modName" placeholder="Enter the name of the mod"/>
<label for="mod-name" class="non-selectable">{{ $t('importing.name') }}</label>
<input id="mod-name" ref="mod-name" class="input margin-right" type="text" v-model="modName" :placeholder="$t('importing.namePH')"/>
</div>
<br/>
<div class="input-group input-group--flex margin-right">
<label for="mod-author" class="non-selectable">Author</label>
<input id="mod-author" ref="mod-author" class="input margin-right" type="text" v-model="modAuthor" placeholder="Enter the author name"/>
<label for="mod-author" class="non-selectable">{{ $t('importing.author') }}</label>
<input id="mod-author" ref="mod-author" class="input margin-right" type="text" v-model="modAuthor" :placeholder="$t('importing.authorPH')"/>
</div>
<br/>
<div class="input-group input-group--flex margin-right">
<label for="mod-author" class="non-selectable">Description (optional)</label>
<input id="mod-description" ref="mod-description" class="input margin-right" type="text" v-model="modDescription" placeholder="Enter a description"/>
<label for="mod-description" class="non-selectable">{{ $t('importing.modDesc') }}</label>
<input id="mod-description" ref="mod-description" class="input margin-right" type="text" v-model="modDescription" :placeholder="$t('importing.modDescPH')"/>
</div>
<hr/>
<h3 class="title is-6">Version</h3>
<h3 class="title is-6">{{ $t('importing.version') }}</h3>
<div class="input-group input-group--flex margin-right non-selectable">
<div class="is-flex">
<div class="margin-right margin-right--half-width">
<label for="mod-version-major">Major</label>
<label for="mod-version-major">{{ $t('importing.major') }}</label>
<input id="mod-version-major" ref="mod-version" class="input margin-right" type="number" v-model="modVersionMajor" min="0" step="1" placeholder="0"/>
</div>
<div class="margin-right margin-right--half-width">
<label for="mod-version-minor">Minor</label>
<label for="mod-version-minor">{{ $t('importing.minor') }}</label>
<input id="mod-version-minor" ref="mod-version" class="input margin-right" type="number" v-model="modVersionMinor" min="0" step="1" placeholder="0"/>
</div>
<div>
<label for="mod-version-patch">Patch</label>
<label for="mod-version-patch">{{ $t('importing.patch') }}</label>
<input id="mod-version-patch" ref="mod-version" class="input margin-right" type="number" v-model="modVersionPatch" min="0" step="1" placeholder="0"/>
</div>
</div>
Expand Down Expand Up @@ -107,8 +107,8 @@ export default class LocalFileImportModal extends Vue {
private async selectFile() {
this.waitingForSelection = true;
InteractionProvider.instance.selectFile({
buttonLabel: "Select file",
title: "Import local mod from file",
buttonLabel: this.$t('importing.selectFileButtonLabel'),
title: this.$t('importing.selectFileTitle'),
filters: []
}).then(value => {
if (value.length > 0) {
Expand Down Expand Up @@ -250,38 +250,38 @@ export default class LocalFileImportModal extends Vue {

switch (0) {
case this.modName.trim().length:
this.validationMessage = "The mod name must not be empty.";
this.validationMessage = this.$t('importing.modNameVM');
return;
case this.modAuthor.trim().length:
this.validationMessage = "The mod author must not be empty.";
this.validationMessage = this.$t('importing.modAuthorVM');
return;
}

switch (NaN) {
case Number(this.modVersionMajor):
case Number(this.modVersionMinor):
case Number(this.modVersionPatch):
this.validationMessage = "Major, minor, and patch must all be numbers.";
this.validationMessage = this.$t('importing.versionVMAll');
return;
}

if (this.modVersionMajor < 0) {
this.validationMessage = "Major, minor, and patch must be whole numbers greater than 0.";
this.validationMessage = this.$t('importing.versionVM');
return;
}
if (this.modVersionMinor < 0) {
this.validationMessage = "Major, minor, and patch must be whole numbers greater than 0.";
this.validationMessage = this.$t('importing.versionVM');
return;
}
if (this.modVersionPatch < 0) {
this.validationMessage = "Major, minor, and patch must be whole numbers greater than 0.";
this.validationMessage = this.$t('importing.versionVM');
return;
}

const profile: Profile|null = this.$store.state.profile.activeProfile;

if (profile === null) {
this.validationMessage = "Profile is not selected";
this.validationMessage = this.$t('importing.profileVM');
return;
}

Expand Down
Loading