Skip to content
Open

i18n #2030

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
48fe06c
Fixed progression to Profiles.vue screen
ebkr Jun 27, 2025
bcd5ca4
WIP: Added i18n translation for platform modal header. Only key is sh…
ebkr Jul 31, 2025
449c034
Translations now show correctly
ebkr Jul 31, 2025
0644120
GameSelectionScreen supports i18n for English and French
ebkr Aug 1, 2025
0a47c65
Added English translation for Splash.vue
ebkr Aug 1, 2025
f28b316
Fixed styling with FAQ section
ebkr Aug 1, 2025
da21d51
WIP: Translated the main content view of the Splash screen to French
ebkr Aug 1, 2025
167d501
Added remaining Splash translations in French
ebkr Aug 1, 2025
9340b5f
Formatted translation value to match formatting of en equivalent
ebkr Aug 1, 2025
4d480c0
Split translations into clearer modules
ebkr Aug 1, 2025
84e218c
Added translations for main view of Profiles.vue
ebkr Aug 4, 2025
1292468
Added i18n support for CreateProfileModal and DeleteProfileModal
ebkr Aug 4, 2025
46ca2b5
Added support for RenameProfileModal
ebkr Aug 5, 2025
0cf3750
Added English translations for ImportProfileModal
ebkr Aug 6, 2025
0ccb856
Added French translations for ImportProfileModal
ebkr Aug 7, 2025
9c7e4c8
WIP: Fixed new import progress text changes from develop
ebkr Aug 27, 2025
6ce7d05
Added English i18n support for NavigationMenu and update banner
ebkr Aug 7, 2025
900bbca
Added English i18n support for the empty mod list message and update …
ebkr Aug 7, 2025
2bd64e3
Added i18n support for the SearchAndSort component
ebkr Aug 7, 2025
fb3c400
WIP: Added Date formatting support. Need to finish porting LocalModCa…
ebkr Aug 7, 2025
ab1e45c
Ported LocalModCard and DonateButton to use i18n
ebkr Aug 7, 2025
ac71a0e
Added ExpandableCard translation
ebkr Aug 7, 2025
96defd2
Added i18n support for loose modals in Manager.vue
ebkr Aug 8, 2025
53150eb
Added i18n support for category filter modal
ebkr Aug 8, 2025
f51a234
Added i18n support for sort modal
ebkr Aug 8, 2025
0a493f4
Added i18n support for local import modal
ebkr Aug 8, 2025
6b89c8b
Added i18n support for code export modal
ebkr Aug 11, 2025
cb633eb
Added i18n support to DownloadProgressModal.vue
ebkr Aug 11, 2025
ffd9eaa
Added i18n support to DownloadModVersionSelectModal
ebkr Aug 11, 2025
edb6b51
Added i18n support to UpdateAllInstalledModsModal
ebkr Aug 11, 2025
34bc10c
Added i18n support to LaunchTypeModal
ebkr Aug 11, 2025
63ba4be
Added i18n support for executable selection actions in Manager.vue
ebkr Aug 11, 2025
8d84f09
Added i18n support for OnlinePreviewPanel
ebkr Aug 12, 2025
a1fa6a6
Added i18n support for OnlineModView
ebkr Aug 12, 2025
7df982a
Added i18n support for OnlineModList
ebkr Aug 12, 2025
a67a426
Added French translations for online view
ebkr Aug 12, 2025
f94745e
Fixed issue after rebase in LaunchTypeModal and improved "Par" capita…
ebkr Aug 27, 2025
6ac3152
Fixed imports and duplicate text after rebase
ebkr Sep 12, 2025
8d65947
Added i18n support for Help.vue
ebkr Aug 13, 2025
e305d2d
Added French translation for Help.vue
ebkr Aug 13, 2025
ff39737
Added missing Hero translations
ebkr Aug 13, 2025
5da996d
Fixed issue after rebase in Help.vue
ebkr Aug 27, 2025
c34d922
WIP: Adding i18n support for SettingsView
ebkr Aug 14, 2025
7ea76b5
Added 'profiles' English translations
ebkr Aug 28, 2025
7411794
Added i18n support for conditional settings
ebkr Aug 28, 2025
a0ff84b
WIP: Adding i18n support for "Other" section
ebkr Aug 28, 2025
6903ed6
Added i18n support for "other" and "modpacks" categories
ebkr Aug 29, 2025
c41e921
Added i18n support for settings tabs
ebkr Aug 29, 2025
75bd871
Added French i18n translation file and fixed non-reactive titles
ebkr Sep 5, 2025
4bd31af
Added missing translations for SettingsView
ebkr Nov 17, 2025
3b3dff1
Added English/French translations for the ConfigEditor screen
ebkr Nov 28, 2025
c3f0b88
Added English/French translations for the Profile Switcher modal
ebkr Nov 28, 2025
2e5d75f
Added English/French translations for DownloadMonitor.vue
ebkr Dec 28, 2025
603ecae
Re-added i18n to game selection after merge and fixed bug
ebkr May 6, 2026
019c122
Updated "Search for a mod" placeholder text after rebase
ebkr Jul 30, 2026
87a8ef1
Resolved translation bugs and fixed immediate missing translations on
ebkr Jul 30, 2026
5cf3b2a
Fixed more typos
ebkr Jul 31, 2026
8b106ee
Added basic language switcher to activity bar
ebkr Jul 31, 2026
a07389a
Added locale display name to language switcher
ebkr Jul 31, 2026
55c286c
Minor correctness fixes to lang switcher
ebkr Jul 31, 2026
6a7ca85
Added translations for ecosystem fetching status
ebkr Jul 31, 2026
3a13776
Reworked translations after settings redesign
ebkr Jul 31, 2026
12e2271
Implemented activity bar translations
ebkr Jul 31, 2026
42965fd
Added translations for new additions to mod lists, and for download
ebkr Aug 3, 2026
6771a1e
Ported more modals
ebkr Aug 3, 2026
87dd9f8
Minor correctness fixes
ebkr Aug 3, 2026
b6ab0ed
More game selection screen translations
ebkr Aug 3, 2026
f345bdc
Translated and refactored banner wording
ebkr Aug 3, 2026
10fccc4
Refactored modals to be in their own definition file
ebkr Aug 3, 2026
bdae8d8
Updated config editor translations
ebkr Aug 3, 2026
ff0a773
Added translations for file selection dialogs
ebkr Aug 4, 2026
e598ccf
Fixed type errors
ebkr Aug 4, 2026
68db666
Added translations for Linux setup screen
ebkr Aug 4, 2026
4eb58a0
Ported SettingsLoader modal
ebkr Aug 4, 2026
8cd7fe9
Finished translating DownloadMonitor
ebkr Aug 4, 2026
efc2bb9
Fixed missed ProfileCodeExportModal and ModalCard translations
ebkr Aug 4, 2026
39cb7b9
Added 404 page translations
ebkr Aug 4, 2026
ec9d90f
Fixed missing translation in DonateIconButton
ebkr Aug 4, 2026
fcd872d
More settings translation fixes and replaced `moment` with i18n date
ebkr Aug 4, 2026
eb02861
ImportProfileModal now references correct translation keypath
ebkr Aug 4, 2026
65cf0c5
Added preparing game translation and fixed bad keypath
ebkr Aug 5, 2026
5ac2a83
Added WIP indicator
ebkr Sep 11, 2026
b2e0b49
Added locale persistence
ebkr Sep 11, 2026
1077bbe
Removed absolute import references
ebkr Sep 11, 2026
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
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
"highlight.js": "^10.4.1",
"js-yaml": "^4.1.1",
"lodash.debounce": "^4.0.8",
"moment": "^2.29.1",
"quasar": "^2.18.6",
"quill": "^1.3.7",
"sanitize-filename": "^1.6.3",
Expand Down Expand Up @@ -87,8 +86,8 @@
"@vue/test-utils": "^2.4.6",
"autoprefixer": "^10.4.21",
"devtron": "^1.4.0",
"electron-builder": "26.14.0",
"electron": "38.8.6",
"electron-builder": "26.14.0",
"electron-debug": "^3.0.1",
"electron-devtools-installer": "^3.0.0",
"identity-obj-proxy": "^3.0.0",
Expand Down
8 changes: 0 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion quasar.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ export default defineConfig((ctx: QuasarContext) => {
'floating-vue',
'vue-i18n',
'lodash.debounce',
'moment',
'quill',
'sanitize-filename',
'vuedraggable',
Expand Down
5 changes: 5 additions & 0 deletions src-electron/preload/hooks-preload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ipcRenderer } from 'electron';

export function once(hookName: string, callback: Function) {
ipcRenderer.once(hookName, (event, ...args) => callback(...args));
}
59 changes: 57 additions & 2 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,33 @@
<router-view />
</div>
</main>
<div id="activity-bar"></div>
<div id="activity-bar">
<div id="activity-bar__left"></div>
<div id="activity-bar__right">
<div>
<ActivityDropdown>
<button type="button" class="activity-bar__action"
:aria-label="`${t('metadata.name', 0, { locale })}`"
>
<i class="fas fa-globe-europe"></i> {{ locale }}
</button>
<template #popper>
<ul class="menu-list">
<li v-for="lang in availableLocales" :key="`locale-select-${lang}`">
<a v-close-popper @click="() => LocaleManager.save(lang)">
<div class="locale-list__item">
<span class="locale-list__text">{{ lang }} ({{ t('metadata.name', 0, {locale: lang}) }})</span>
<span class="locale-list__divider"></span>
<span class="locale-list__tag tag is-warning" v-if="((messages[lang]?.metadata as any).wip || '').length > 0">{{ t('metadata.wip', 0, {locale: lang}) }}</span>
</div>
</a>
</li>
</ul>
</template>
</ActivityDropdown>
</div>
</div>
</div>
</div>
<ErrorModal />
</div>
Expand Down Expand Up @@ -59,13 +85,16 @@ import { ProtocolProviderImplementation } from './providers/generic/protocol/Pro
import { provideProtocolImplementation } from './providers/generic/protocol/ProtocolProvider';
import GameImageProvider, { provideGameImageImplementation } from './providers/generic/image/GameImageProvider';
import { GameImageProviderImplementation } from './r2mm/image/GameImageProviderImpl';
import BreadcrumbNavigator from 'components/breadcrumbs/BreadcrumbNavigator.vue';
import { useI18n } from 'vue-i18n';
import { ActivityDropdown } from './components/all';
import LocaleManager from './r2mm/manager/LocaleManager';

const store = baseStore;
const router = useRouter();
provideStoreImplementation(() => store);

const quasar = useQuasar();
const { t, locale, availableLocales, messages } = useI18n();

document.addEventListener('auxclick', e => {
const target = e.target! as any;
Expand Down Expand Up @@ -109,6 +138,8 @@ BindLoaderImpl.bind();
onMounted(async () => {
const settings: ManagerSettings = await store.dispatch('resetActiveGame');

await LocaleManager.apply();

hookBackgroundUpdateThunderstoreModList(router);
hookModInstallingViaProtocol(router);
await checkCdnConnection();
Expand Down Expand Up @@ -201,5 +232,29 @@ main {
gap: 0.5rem;
overflow: hidden;
}

&__left {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
gap: 0.375rem;
}

&__right {
display: flex;
flex-direction: row-reverse;
gap: 1rem;
align-items: center;
}
}

.locale-list__item {
display: flex;
place-items: center;

.locale-list__divider {
flex: 1;
}
}
</style>
12 changes: 1 addition & 11 deletions src/boot/i18n.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
import { createI18n } from 'vue-i18n';
import messages from '../i18n';
import { defineBoot } from '#q-app/wrappers';
// You'll need to create the src/i18n/index.js/.ts file too

const i18n = createI18n({
locale: 'en-US',
globalInjection: true,
allowComposition: true,
legacy: false,
messages,
})
import { i18n } from '../i18n/instance';

// @ts-ignore
export default defineBoot(async ({ app }) => {
Expand Down
12 changes: 7 additions & 5 deletions src/components/ExpandableCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<header class='card-header is-shadowless' :id='id'>
<div class='card-header-icon mod-logo' v-if="image !== ''">
<figure class='image is-48x48 image-parent'>
<img :src='image' alt='Mod Logo' class='image-overlap'/>
<img v-if="store.state.profile.funkyMode" :src='ProtocolProvider.getPublicAssetUrl("/funky_mode.png")' alt='Funky mode' class='image-overlap'/>
<img :src='image' :alt="t('translations.pages.manager.installed.expandableCard.imageAltText')" class='image-overlap'/>
<img v-if="store.state.profile.funkyMode" :src='ProtocolProvider.getPublicAssetUrl("/funky_mode.png")' :alt="t('translations.pages.manager.installed.expandableCard.funkyModeAltText')" class='image-overlap'/>
</figure>
</div>
<span ref="title" class='card-header-title expandable-card__title'>
Expand All @@ -16,12 +16,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('translations.pages.manager.installed.expandableCard.tooltips.dragToReorder')"></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('translations.pages.manager.installed.expandableCard.tooltips.expand')"></i>
<i class='fas fa-angle-down' aria-hidden='true' v-if='visible' v-tooltip.left="t('translations.pages.manager.installed.expandableCard.tooltips.collapse')"></i>
</span>
</a>
</header>
Expand All @@ -45,9 +45,11 @@
import { computed, onMounted, ref, watchEffect } from 'vue';
import { getStore } from '../providers/generic/store/StoreProvider';
import { State } from '../store';
import { useI18n } from "vue-i18n";
import ProtocolProvider from '../providers/generic/protocol/ProtocolProvider';

const store = getStore<State>();
const { t } = useI18n();

type ExpandableCardProps = {
image?: string;
Expand Down
28 changes: 16 additions & 12 deletions src/components/ModListUpdateBanner.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
<script lang="ts" setup>
import R2Error from '../model/errors/R2Error';
import { useI18n } from 'vue-i18n';
import { computed } from 'vue';
import { getStore } from '../providers/generic/store/StoreProvider';
import { State } from '../store';


const { t } = useI18n();

const store = getStore<State>();

const isModListLoaded = computed<boolean>(() => store.state.tsMods.modsLastUpdated !== undefined);
Expand All @@ -26,24 +30,24 @@ function openErrorModal() {
<div v-if="!isModListLoaded" id="mod-list-update-banner" class="margin-bottom">
<div class="notification is-warning margin-right">
<span v-if="isUpdateInProgress">
{{ store.state.tsMods.thunderstoreModListUpdateStatus }}
{{ t(`translations.modListStatus.${store.state.tsMods.thunderstoreModListUpdateStatus}`) }}
</span>
<span v-else-if="updateError">
Error refreshing the mod list.
<a @click="openErrorModal">View error details</a>.
{{ t('translations.banners.modListUpdate.error') }}
<a @click="openErrorModal">{{ t('translations.banners.modListUpdate.viewDetails') }}</a>.
<br />
The manager will keep trying to refresh the mod list in the background.
{{ t('translations.banners.modListUpdate.willKeepTrying') }}
</span>
<span v-else-if="store.getters['download/activeDownloadCount'] > 0">
An error occurred when refreshing the mod list from Thunderstore.<br />
However, the mod list can't be refreshed while the are mod downloads in progress.<br />
Please wait for the downloads to finish before continuing.
</span>
<span v-else>
An error occurred when refreshing the mod list from Thunderstore.
Would you like to
<a @click="updateModList">try again now</a>?
{{ t('translations.banners.modListUpdate.errorOccurred') }}<br />
{{ t('translations.banners.modListUpdate.blockedByDownloads') }}<br />
{{ t('translations.banners.modListUpdate.waitForDownloads') }}
</span>
<i18n-t v-else tag="span" keypath="translations.banners.modListUpdate.retryPrompt">
<template v-slot:retryAction>
<a @click="updateModList">{{ t('translations.banners.modListUpdate.retryAction') }}</a>
</template>
</i18n-t>
</div>
</div>
</template>
Expand Down
6 changes: 5 additions & 1 deletion src/components/ModalCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
</footer>
</template>
</div>
<button v-if="canClose" class="modal-close is-large" aria-label="close" @click="closeModal"/>
<button v-if="canClose" class="modal-close is-large" :aria-label="t('translations.modals.actions.close')" @click="closeModal"/>
</div>
</template>

<script lang="ts" setup>
import { useI18n } from 'vue-i18n';

const { t } = useI18n();

type ModalCardProps = {
id: string;
isActive?: boolean;
Expand Down
41 changes: 19 additions & 22 deletions src/components/SettingsLoader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,40 @@
<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('translations.modals.error.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('translations.modals.error.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('translations.modals.settingsLoader.managerProblem') }}
</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('translations.modals.settingsLoader.loadFailed') }}
</p>
<br />
<button @click="resetSettings" :disabled="resettingInProgress" class="button is-white">
Reset settings
{{ t('translations.modals.settingsLoader.resetAction') }}
</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
<a @click="openLink('https://github.com/ebkr/r2modmanPlus/wiki/Error:-White-or-blank-game-select-screen-on-startup#corrupted-settings-on-update')">
instructions.
</a>
</p>
<i18n-t
v-else-if="phase === PHASES.RESET_FAILED"
tag="p"
keypath="translations.modals.settingsLoader.resetFailed"
>
<template v-slot:instructionsLink>
<a @click="openLink('https://github.com/ebkr/r2modmanPlus/wiki/Error:-White-or-blank-game-select-screen-on-startup#corrupted-settings-on-update')">
{{ t('translations.modals.settingsLoader.instructionsLinkText') }}
</a>
</template>
</i18n-t>

<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('translations.modals.settingsLoader.resetDidNotHelp') }}
</p>
</div>
</div>
Expand All @@ -53,6 +48,7 @@

<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';

import R2Error from "../model/errors/R2Error";
import Game from "..//model/game/Game";
Expand All @@ -77,6 +73,7 @@ type SettingsLoaderType = {
}

const props = defineProps<SettingsLoaderType>();
const { t } = useI18n();

const error = ref<R2Error|null>(null);
const phase = ref<PHASES>(PHASES.INITIAL);
Expand Down
6 changes: 5 additions & 1 deletion src/components/banner/ConcerningPackageBanner.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
<script setup lang="ts">

import { getStore } from '@r2/providers/generic/store/StoreProvider';
import { useI18n } from 'vue-i18n';
import { State } from '@r2/store';
import { useConcerningPackageComposable } from '@r2/components/composables/ConcerningPackageComposable';


const { t } = useI18n();

const store = getStore<State>();
const { hasConcerningPackages } = useConcerningPackageComposable();

Expand All @@ -14,7 +18,7 @@ function addUnlinkedFilter() {

<template>
<div class="notification is-concern margin-right" v-show="hasConcerningPackages">
<span>You have mods that can no longer be found on Thunderstore.</span> <a href="#" @click.stop.prevent="addUnlinkedFilter">Click here to review packages.</a>
<span>{{ t('translations.banners.concerningPackage.text') }}</span> <a href="#" @click.stop.prevent="addUnlinkedFilter">{{ t('translations.banners.concerningPackage.action') }}</a>
</div>
</template>

Expand Down
Loading
Loading