fix: localization fixes

This commit is contained in:
2025-04-27 20:30:22 +00:00
parent 240f60b283
commit 91d38b58c4
10 changed files with 99 additions and 77 deletions

View File

@ -7,12 +7,20 @@ use crate::pkg_store::PackageStore;
use crate::util; use crate::util;
use crate::profiles::types::ProfilePaths; use crate::profiles::types::ProfilePaths;
pub async fn prepare_packages<'a>(p: &'a impl ProfilePaths, pkgs: &BTreeSet<PkgKey>, redo_bepinex: bool) -> Result<()> { pub async fn prepare_packages<'a>(p: &'a impl ProfilePaths, pkgs: &BTreeSet<PkgKey>, mut redo_bepinex: bool) -> Result<()> {
log::debug!("begin prepare packages"); log::debug!("begin prepare packages");
let pfx_dir = p.data_dir(); let pfx_dir = p.data_dir();
let opt_dir = pfx_dir.join("option"); let opt_dir = pfx_dir.join("option");
for m in pkgs {
let (namespace, _) = m.split()?;
if namespace == "local" {
log::info!("package with the 'local' namespace enabled -- force refreshing");
redo_bepinex = true;
}
}
if redo_bepinex { if redo_bepinex {
if pfx_dir.join("BepInEx").exists() { if pfx_dir.join("BepInEx").exists() {
util::remove_dir_all(pfx_dir.join("BepInEx")).await?; util::remove_dir_all(pfx_dir.join("BepInEx")).await?;

View File

@ -27,6 +27,9 @@ import {
usePrfStore, usePrfStore,
} from '../stores'; } from '../stores';
import { messageSplit, shouldPreferDark } from '../util'; import { messageSplit, shouldPreferDark } from '../util';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
document.documentElement.classList.toggle('use-dark-mode', shouldPreferDark()); document.documentElement.classList.toggle('use-dark-mode', shouldPreferDark());
@ -257,7 +260,7 @@ listen<DownloadingStatus>('download-progress', (event) => {
style="min-width: 0; width: 25dvw" style="min-width: 0; width: 25dvw"
class="self-center" class="self-center"
size="small" size="small"
placeholder="Search" :placeholder="t('search')"
v-model="general.cfgSearchTerm" v-model="general.cfgSearchTerm"
/> />
<InputText <InputText
@ -265,7 +268,7 @@ listen<DownloadingStatus>('download-progress', (event) => {
style="min-width: 0; width: 25dvw" style="min-width: 0; width: 25dvw"
class="self-center" class="self-center"
size="small" size="small"
placeholder="Search" :placeholder="t('search')"
v-model="pkgSearchTerm" v-model="pkgSearchTerm"
/> />
</div> </div>

View File

@ -101,7 +101,7 @@ const installRecommended = () => {
<MultiSelect <MultiSelect
size="small" size="small"
:showToggleAll="false" :showToggleAll="false"
placeholder="Include categories" :placeholder="t('store.includeCategories')"
v-model="pkgs.includeCategories" v-model="pkgs.includeCategories"
:options="[...pkgs.availableCategories]" :options="[...pkgs.availableCategories]"
class="w-full" class="w-full"
@ -109,7 +109,7 @@ const installRecommended = () => {
<MultiSelect <MultiSelect
size="small" size="small"
:showToggleAll="false" :showToggleAll="false"
placeholder="Exclude categories" :placeholder="t('store.excludeCategories')"
v-model="pkgs.excludeCategories" v-model="pkgs.excludeCategories"
:options="[...pkgs.availableCategories]" :options="[...pkgs.availableCategories]"
class="w-full" class="w-full"

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ComputedRef, computed, onMounted, ref } from 'vue'; import { ComputedRef, computed, ref } from 'vue';
import Button from 'primevue/button'; import Button from 'primevue/button';
import Carousel from 'primevue/carousel'; import Carousel from 'primevue/carousel';
import Dialog from 'primevue/dialog'; import Dialog from 'primevue/dialog';
@ -27,7 +27,7 @@ interface Datum {
const game = computed(() => prf.current?.meta.game); const game = computed(() => prf.current?.meta.game);
const processText = (s: string) => { const processText = computed(() => (s: string) => {
// Why do I have to do this // Why do I have to do this
s = s s = s
.split('\n') .split('\n')
@ -39,14 +39,14 @@ const processText = (s: string) => {
if (readable !== null) { if (readable !== null) {
return s.replace( return s.replace(
'%TESTMENU%', '%TESTMENU%',
`${readable} or a button on the back of the controller` `${readable} ${t('onboarding.or')} ${t('onboarding.backButton')}`
); );
} }
} }
return s.replace('%TESTMENU%', 'a button on the back of the controller'); return s.replace('%TESTMENU%', t('onboarding.backButton'));
}; });
const loadPage = (title: string, messages?: object) => { const loadPage = computed(() => (title: string, messages?: object) => {
return { return {
text: t(`onboarding.${title}`, { text: t(`onboarding.${title}`, {
endlink: '</a>', endlink: '</a>',
@ -56,19 +56,44 @@ const loadPage = (title: string, messages?: object) => {
}), }),
image: `help-${title}.png`, image: `help-${title}.png`,
}; };
}; });
let systemProcessing: Datum;
let standardOngeki: Datum;
let standardChunithm: Datum;
let lever: Datum;
let server: Datum;
let finaleOngeki: Datum;
let finaleChunithm: Datum;
const data: ComputedRef<Datum[]> = computed(() => { const data: ComputedRef<Datum[]> = computed(() => {
const res = []; const res = [];
const [standard, systemProcessing, lever, server, finale] = [
loadPage.value('standard', {
bigblack: '<div class="p-2 mt-1 mb-1 bg-black text-white">',
endbig: '</div>',
}),
loadPage.value('ongeki-system-processing'),
loadPage.value('ongeki-lever'),
loadPage.value('chunithm-server', {
link: '<a href="https://gitea.tendokyu.moe/Dniel97/SEGAguide/wiki/FAQ#game-is-stuck-at-checking-distribution-server" target="_blank">',
}),
loadPage.value('finale', {
segaguide:
'<a href="https://gitea.tendokyu.moe/Dniel97/SEGAguide/wiki/FAQ" target="_blank">',
twotorial: '<a href="https://two-torial.xyz/" target="_blank">',
}),
];
const standardOngeki = {
...standard,
image: '/help-standard-ongeki.png',
};
const standardChunithm = {
...standard,
image: '/help-standard-chunithm.png',
};
const finaleOngeki = {
...finale,
image: '/help-finale-ongeki.png',
};
const finaleChunithm = {
...finale,
image: '/help-finale-chunithm.png',
};
switch (prf.current?.meta.game) { switch (prf.current?.meta.game) {
case 'ongeki': case 'ongeki':
res.push(systemProcessing); res.push(systemProcessing);
@ -92,48 +117,14 @@ const context = ref({
index: 0, index: 0,
}); });
onMounted(async () => {
[standardOngeki, systemProcessing, lever, server, finaleOngeki] =
await Promise.all([
loadPage('standard', {
bigblack: '<div class="p-2 mt-1 mb-1 bg-black text-white">',
endbig: '</div>',
}),
loadPage('ongeki-system-processing'),
loadPage('ongeki-lever'),
loadPage('chunithm-server', {
link: '<a href="https://gitea.tendokyu.moe/Dniel97/SEGAguide/wiki/FAQ#game-is-stuck-at-checking-distribution-server" target="_blank">',
}),
loadPage('finale', {
segaguide:
'<a href="https://gitea.tendokyu.moe/Dniel97/SEGAguide/wiki/FAQ" target="_blank">',
twotorial: '<a href="https://two-torial.xyz/" target="_blank">',
}),
]);
standardOngeki = {
...standardOngeki,
image: '/help-standard-ongeki.png',
};
standardChunithm = {
...standardOngeki,
image: '/help-standard-chunithm.png',
};
finaleOngeki = {
...finaleOngeki,
image: '/help-finale-ongeki.png',
};
finaleChunithm = {
...finaleOngeki,
image: '/help-finale-chunithm.png',
};
});
const exitLabel = computed(() => { const exitLabel = computed(() => {
return props.firstTime === true && return props.firstTime === true &&
context.value.index < data.value.length - 1 context.value.index < data.value.length - 1
? 'Skip' ? t('skip')
: 'Close'; : t('close');
}); });
const page = ref(0);
</script> </script>
<template> <template>
@ -147,14 +138,15 @@ const exitLabel = computed(() => {
: `${game ? prettyPrint(game) : '<game>'} help` : `${game ? prettyPrint(game) : '<game>'} help`
" "
:style="{ width: '760px', scale: client.scaleValue }" :style="{ width: '760px', scale: client.scaleValue }"
v-on:show="() => (context.index = 0)" v-on:show="() => ((context.index = 0), (page = 0))"
> >
<Carousel <Carousel
:value="data" :value="data"
:num-visible="1" :num-visible="1"
:num-scroll="1" :num-scroll="1"
:context="context" :context="context"
v-on:update:page="(p) => (context.index = p)" :page="page"
v-on:update:page="(p) => ((context.index = p), (page = p))"
> >
<template #item="slotProps"> <template #item="slotProps">
<div class="md-container markdown"> <div class="md-container markdown">
@ -178,8 +170,8 @@ const exitLabel = computed(() => {
<Button <Button
v-if="context.index < data.length - 1" v-if="context.index < data.length - 1"
class="m-auto mr-4" class="m-auto mr-4"
label="Next" :label="t('next')"
@click="() => (context.index += 1)" @click="() => (page += 1)"
/> />
<Button <Button
class="m-auto" class="m-auto"

View File

@ -57,7 +57,7 @@ invoke('list_patches', { target: prf.current!.data.sgt.target }).then(
v-for="p in amdPatches" v-for="p in amdPatches"
:patch="p" :patch="p"
/> />
<div v-if="gamePatches === null">Loading...</div> <div v-if="gamePatches === null">{{ t('patch.loading') }}</div>
<div v-if="amdPatches !== null && amdPatches.length === 0"> <div v-if="amdPatches !== null && amdPatches.length === 0">
{{ t('patch.noneFound') }} {{ t('patch.noneFound') }}
<!-- <br /> <!-- <br />

View File

@ -150,14 +150,14 @@ const importPick = async () => {
</div> </div>
<div class="mt-4 flex flex-row flex-wrap align-middle gap-4"> <div class="mt-4 flex flex-row flex-wrap align-middle gap-4">
<Button <Button
label="Import template" :label="t('profile.import')"
icon="pi pi-file-import" icon="pi pi-file-import"
class="import-button profile-button" class="import-button profile-button"
@click="() => importPick()" @click="() => importPick()"
/> />
<Button <Button
:disabled="prf.current === null" :disabled="prf.current === null"
label="Export template" :label="t('profile.export')"
icon="pi pi-file-export" icon="pi pi-file-export"
class="profile-button" class="profile-button"
@click="() => openExportDialog()" @click="() => openExportDialog()"
@ -179,6 +179,7 @@ const importPick = async () => {
:options="[ :options="[
{ title: 'English', value: 'en' }, { title: 'English', value: 'en' },
// { title: '日本語', value: 'ja' }, // { title: '日本語', value: 'ja' },
{ title: 'Polski', value: 'pl' },
]" ]"
size="small" size="small"
option-label="title" option-label="title"

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { Ref, computed, ref } from 'vue'; import { Ref, computed, onMounted, ref } from 'vue';
import InputNumber from 'primevue/inputnumber'; import InputNumber from 'primevue/inputnumber';
import Select from 'primevue/select'; import Select from 'primevue/select';
import SelectButton from 'primevue/selectbutton'; import SelectButton from 'primevue/selectbutton';
@ -65,7 +65,9 @@ const loadDisplays = () => {
.catch(() => {}); .catch(() => {});
}; };
onMounted(() => {
loadDisplays(); loadDisplays();
});
const game = computed(() => prf.current!.meta.game); const game = computed(() => prf.current!.meta.game);
const isVertical = computed(() => prf.current!.meta.game === 'ongeki'); const isVertical = computed(() => prf.current!.meta.game === 'ongeki');
@ -76,7 +78,7 @@ const canSkipPrimarySwitch = computed(
</script> </script>
<template> <template>
<OptionCategory title="Display"> <OptionCategory :title="t('cfg.display.title')">
<OptionRow <OptionRow
v-if="capabilities.includes('display')" v-if="capabilities.includes('display')"
:title="t('cfg.display.target')" :title="t('cfg.display.target')"
@ -92,7 +94,7 @@ const canSkipPrimarySwitch = computed(
</OptionRow> </OptionRow>
<OptionRow <OptionRow
class="number-input" class="number-input"
title="Game resolution" :title="t('cfg.display.resolution')"
v-if="adjustableRez" v-if="adjustableRez"
> >
<InputNumber <InputNumber
@ -117,9 +119,9 @@ const canSkipPrimarySwitch = computed(
<SelectButton <SelectButton
v-model="prf.current!.data.display.mode" v-model="prf.current!.data.display.mode"
:options="[ :options="[
{ title: 'Window', value: 'Window' }, { title: t('cfg.display.window'), value: 'Window' },
{ title: 'Borderless window', value: 'Borderless' }, { title: t('cfg.display.borderless'), value: 'Borderless' },
{ title: 'Fullscreen', value: 'Fullscreen' }, { title: t('cfg.display.fullscreen'), value: 'Fullscreen' },
]" ]"
:allow-empty="false" :allow-empty="false"
option-label="title" option-label="title"

View File

@ -147,9 +147,9 @@ const checkSegatoolsIni = async (target: string) => {
<Select <Select
v-model="prf.current!.data.sgt.io2" v-model="prf.current!.data.sgt.io2"
:options="[ :options="[
{ title: 'native io4', value: 'hardware' }, { title: t('cfg.segatools.io4'), value: 'hardware' },
{ {
title: 'segatools built-in (keyboard)', title: t('cfg.segatools.ioBuiltIn'),
value: 'segatools_built_in', value: 'segatools_built_in',
}, },
...pkgs ...pkgs

View File

@ -1,8 +1,7 @@
import en from './i18n/en'; import en from './i18n/en';
import ja from './i18n/ja';
import { createI18n } from 'vue-i18n'; import { createI18n } from 'vue-i18n';
export type Locale = 'en' | 'ja'; export type Locale = 'en' | 'ja' | 'pl';
const loadLocaleMessages = async (locale: Locale) => { const loadLocaleMessages = async (locale: Locale) => {
return (await import(`./i18n/${locale}.ts`)).default; return (await import(`./i18n/${locale}.ts`)).default;
@ -14,7 +13,7 @@ const i18n = createI18n({
fallbackLocale: 'en', fallbackLocale: 'en',
warnHtmlInMessage: false, warnHtmlInMessage: false,
warnHtmlMessage: false, warnHtmlMessage: false,
messages: { en, ja }, messages: { en, ja: {}, pl: {} },
}); });
const setLocale = async (locale: Locale) => { const setLocale = async (locale: Locale) => {

View File

@ -4,6 +4,10 @@ export default {
enable: 'Enable', enable: 'Enable',
disable: 'Disable', disable: 'Disable',
default: 'Default', default: 'Default',
search: 'Search',
next: 'Next',
skip: 'Skip',
close: 'Close',
start: { start: {
failed: 'Start check failed', failed: 'Start check failed',
accept: 'Run anyway', accept: 'Run anyway',
@ -38,6 +42,8 @@ export default {
delete: 'Delete profile', delete: 'Delete profile',
reallyDelete: 'Are you sure you want to delete {profile}?', reallyDelete: 'Are you sure you want to delete {profile}?',
template: 'STARTLINER template', template: 'STARTLINER template',
import: 'Import template',
export: 'Export template',
}, },
store: { store: {
installRecommended: 'Install recommended packages', installRecommended: 'Install recommended packages',
@ -46,6 +52,8 @@ export default {
nsfw: 'Show NSFW', nsfw: 'Show NSFW',
incompatible: 'This package is currently incompatible with STARTLINER.', incompatible: 'This package is currently incompatible with STARTLINER.',
missing: 'Missing', missing: 'Missing',
includeCategories: 'Include categories',
excludeCategories: 'Exclude categories',
}, },
patch: { patch: {
loading: 'Loading...', loading: 'Loading...',
@ -70,9 +78,13 @@ export default {
hooks: 'Hooks', hooks: 'Hooks',
ioModules: 'IO modules', ioModules: 'IO modules',
ioModulesDesc: 'This should match your desired input method.', ioModulesDesc: 'This should match your desired input method.',
ioBuiltIn: 'segatools built-in (keyboard)',
io4: 'Native IO4',
installTooltip: '{thing} can be downloaded from the package store.', installTooltip: '{thing} can be downloaded from the package store.',
}, },
display: { display: {
title: 'Display',
resolution: 'Game resolution',
primary: 'Primary', primary: 'Primary',
target: 'Target display', target: 'Target display',
mode: 'Mode', mode: 'Mode',
@ -88,6 +100,9 @@ export default {
portrait: 'Portrait', portrait: 'Portrait',
landscape: 'Landscape', landscape: 'Landscape',
flipped: 'flipped', flipped: 'flipped',
window: 'Window',
borderless: 'Borderless window',
fullscreen: 'Fullscreen',
}, },
network: { network: {
title: 'Network', title: 'Network',
@ -160,6 +175,8 @@ export default {
}, },
}, },
onboarding: { onboarding: {
or: 'or',
backButton: 'a button on the back of the controller',
standard: ` standard: `
You might get stuck on the following screen: You might get stuck on the following screen: