Merge branch 'master' into openid-connect

This commit is contained in:
Georges-Antoine Assi
2024-12-13 11:24:04 -05:00
136 changed files with 2181 additions and 509 deletions

View File

@@ -125,7 +125,7 @@ def get_user(request: Request, id: int) -> UserSchema:
return UserSchema.model_validate(user)
@protected_route(router.put, "/users/{id}", [Scope.USERS_WRITE])
@protected_route(router.put, "/users/{id}", [Scope.ME_WRITE])
async def update_user(
request: Request, id: int, form_data: Annotated[UserForm, Depends()]
) -> UserSchema:

View File

@@ -28,6 +28,7 @@
"semver": "^7.6.2",
"socket.io-client": "^4.7.5",
"vue": "^3.4.27",
"vue-i18n": "^10.0.5",
"vue-router": "^4.3.2",
"vuetify": "^3.7.4",
"webfontloader": "^1.6.28"
@@ -2217,6 +2218,47 @@
"dev": true,
"license": "BSD-3-Clause"
},
"node_modules/@intlify/core-base": {
"version": "10.0.5",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.5.tgz",
"integrity": "sha512-F3snDTQs0MdvnnyzTDTVkOYVAZOE/MHwRvF7mn7Jw1yuih4NrFYLNYIymGlLmq4HU2iIdzYsZ7f47bOcwY73XQ==",
"dependencies": {
"@intlify/message-compiler": "10.0.5",
"@intlify/shared": "10.0.5"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/message-compiler": {
"version": "10.0.5",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.5.tgz",
"integrity": "sha512-6GT1BJ852gZ0gItNZN2krX5QAmea+cmdjMvsWohArAZ3GmHdnNANEcF9JjPXAMRtQ6Ux5E269ymamg/+WU6tQA==",
"dependencies": {
"@intlify/shared": "10.0.5",
"source-map-js": "^1.0.2"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/shared": {
"version": "10.0.5",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.5.tgz",
"integrity": "sha512-bmsP4L2HqBF6i6uaMqJMcFBONVjKt+siGluRq4Ca4C0q7W2eMaVZr8iCgF9dKbcVXutftkC7D6z2SaSMmLiDyA==",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.5",
"devOptional": true,
@@ -7323,6 +7365,25 @@
"eslint": ">=6.0.0"
}
},
"node_modules/vue-i18n": {
"version": "10.0.5",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.5.tgz",
"integrity": "sha512-9/gmDlCblz3i8ypu/afiIc/SUIfTTE1mr0mZhb9pk70xo2csHAM9mp2gdQ3KD2O0AM3Hz/5ypb+FycTj/lHlPQ==",
"dependencies": {
"@intlify/core-base": "10.0.5",
"@intlify/shared": "10.0.5",
"@vue/devtools-api": "^6.5.0"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
},
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/vue-router": {
"version": "4.3.2",
"license": "MIT",

View File

@@ -46,6 +46,7 @@
"semver": "^7.6.2",
"socket.io-client": "^4.7.5",
"vue": "^3.4.27",
"vue-i18n": "^10.0.5",
"vue-router": "^4.3.2",
"vuetify": "^3.7.4",
"webfontloader": "^1.6.28"

View File

@@ -5,12 +5,25 @@ import storeHeartbeat from "@/stores/heartbeat";
import api from "@/services/api/index";
import userApi from "@/services/api/user";
import router from "@/plugins/router";
import { onBeforeMount } from "vue";
import languageStore from "@/stores/language";
import { storeToRefs } from "pinia";
import { onBeforeMount, ref } from "vue";
import { useI18n } from "vue-i18n";
// Props
const heartbeat = storeHeartbeat();
const auth = storeAuth();
const configStore = storeConfig();
const { locale } = useI18n();
const storeLanguage = languageStore();
const { defaultLanguage, languages } = storeToRefs(storeLanguage);
const selectedLanguage = ref(
languages.value.find(
(lang) => lang.value === localStorage.getItem("settings.locale"),
) || defaultLanguage.value,
);
locale.value = selectedLanguage.value.value;
storeLanguage.setLanguage(selectedLanguage.value);
// Functions
onBeforeMount(async () => {

View File

@@ -6,8 +6,10 @@ import storeDownload from "@/stores/download";
import type { DetailedRom } from "@/stores/roms";
import { formatBytes } from "@/utils";
import { ref, watch } from "vue";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const props = defineProps<{ rom: DetailedRom }>();
const downloadStore = storeDownload();
const romUser = ref(props.rom.rom_user);
@@ -39,8 +41,8 @@ watch(
class="align-center my-3"
no-gutters
>
<v-col cols="3" xl="2">
<span>Version</span>
<v-col cols="3" xl="2" class="mr-2">
<span>{{ t("rom.version") }}</span>
</v-col>
<v-col>
<v-row class="align-center" no-gutters>
@@ -49,7 +51,7 @@ watch(
location="top"
class="tooltip"
transition="fade-transition"
text="Set as default version"
:text="t('rom.set-as-default')"
open-delay="300"
>
<template #activator="{ props }">
@@ -58,7 +60,6 @@ watch(
variant="flat"
rounded="0"
size="small"
class="ml-2"
@click="toggleMainSibling"
><v-icon
:class="romUser.is_main_sibling ? '' : 'mr-1'"
@@ -68,7 +69,7 @@ watch(
? "mdi-checkbox-outline"
: "mdi-checkbox-blank-outline"
}}</v-icon
>{{ romUser.is_main_sibling ? "" : "Default" }}</v-btn
>{{ romUser.is_main_sibling ? "" : t("rom.default") }}</v-btn
>
</template></v-tooltip
>
@@ -76,16 +77,16 @@ watch(
</v-col>
</v-row>
<v-row v-if="!rom.multi" class="align-center my-3" no-gutters>
<v-col cols="3" xl="2">
<span>File</span>
<v-col cols="3" xl="2" class="mr-2">
<span>{{ t("rom.file") }}</span>
</v-col>
<v-col>
<span class="text-body-1">{{ rom.file_name }}</span>
</v-col>
</v-row>
<v-row v-if="rom.multi" class="align-center my-3" no-gutters>
<v-col cols="3" xl="2">
<span>Files</span>
<v-col cols="3" xl="2" class="mr-2">
<span>{{ t("rom.files") }}</span>
</v-col>
<v-col>
<v-select
@@ -105,14 +106,14 @@ watch(
</v-col>
</v-row>
<v-row no-gutters class="align-center my-3">
<v-col cols="3" xl="2">
<span>Info</span>
<v-col cols="3" xl="2" class="mr-2">
<span>{{ t("rom.info") }}</span>
</v-col>
<v-col class="my-1">
<v-row no-gutters>
<v-col cols="12">
<v-chip size="small" class="mr-2 px-0" label>
<v-chip label>Size</v-chip
<v-chip label>{{ t("rom.size") }}</v-chip
><span class="px-2">{{
formatBytes(rom.file_size_bytes)
}}</span>
@@ -132,8 +133,8 @@ watch(
</v-col>
</v-row>
<v-row v-if="rom.tags.length > 0" class="align-center my-3" no-gutters>
<v-col cols="3" xl="2">
<span>Tags</span>
<v-col cols="3" xl="2" class="mr-2">
<span>{{ t("rom.tags") }}</span>
</v-col>
<v-col>
<v-chip

View File

@@ -7,14 +7,21 @@ import { storeToRefs } from "pinia";
import { ref } from "vue";
import { useRouter } from "vue-router";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const props = defineProps<{ rom: DetailedRom }>();
const { xs } = useDisplay();
const show = ref(false);
const carousel = ref(0);
const router = useRouter();
const filters = ["genres", "franchises", "collections", "companies"] as const;
const filters = [
{ value: "genres", name: t("rom.genres") },
{ value: "franchises", name: t("rom.franchises") },
{ value: "collections", name: t("rom.collections") },
{ value: "companies", name: t("rom.companies") },
] as const;
const galleryViewStore = storeGalleryView();
const { defaultAspectRatioScreenshot } = storeToRefs(galleryViewStore);
@@ -36,7 +43,7 @@ function onFilterClick(filter: FilterType, value: string) {
no-gutters
class="align-center my-3"
>
<v-col cols="3" xl="2">
<v-col cols="3" xl="2" class="mr-2">
<span>RomM Collections</span>
</v-col>
<v-col>
@@ -62,18 +69,18 @@ function onFilterClick(filter: FilterType, value: string) {
</v-row>
<template v-for="filter in filters" :key="filter">
<v-row
v-if="rom[filter].length > 0"
v-if="rom[filter.value].length > 0"
class="align-center my-3"
no-gutters
>
<v-col cols="3" xl="2" class="text-capitalize">
<span>{{ filter }}</span>
<v-col cols="3" xl="2" class="text-capitalize mr-2">
<span>{{ filter.name }}</span>
</v-col>
<v-col>
<v-chip
v-for="value in rom[filter]"
v-for="value in rom[filter.value]"
:key="value"
@click="onFilterClick(filter, value)"
@click="onFilterClick(filter.value, value)"
size="small"
variant="outlined"
class="my-1 mr-2"
@@ -95,7 +102,7 @@ function onFilterClick(filter: FilterType, value: string) {
<v-col cols="3" xl="2" class="text-capitalize">
<span>Age Rating</span>
</v-col>
<div class="d-flex">
<div class="d-flex" :class="{ 'my-2': xs }">
<v-img
v-for="value in rom.igdb_metadata.age_ratings"
:key="value.rating"

View File

@@ -8,7 +8,10 @@ import { MdEditor, MdPreview } from "md-editor-v3";
import "md-editor-v3/lib/style.css";
import { ref, watch } from "vue";
import { useDisplay, useTheme } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const props = defineProps<{ rom: DetailedRom }>();
const auth = storeAuth();
const theme = useTheme();
@@ -26,6 +29,7 @@ const statusOptions = [
"completed_100",
];
// Functions
function editNote() {
if (editingNote.value) {
romApi.updateUserRomProps({
@@ -61,7 +65,7 @@ watch(
<v-card rounded="0" class="mb-2">
<v-card-title class="bg-terciary">
<v-list-item class="pl-2 pr-0">
<span class="text-h6">Status</span>
<span class="text-h6">{{ t("rom.status") }}</span>
</v-list-item>
</v-card-title>
<v-card-text class="px-8 py-4">
@@ -77,7 +81,7 @@ watch(
hide-details
>
<template #label
><span>Backlogged</span
><span>{{ t("rom.backlogged") }}</span
><span class="ml-2">{{
getEmojiForStatus("backlogged")
}}</span></template
@@ -89,7 +93,7 @@ watch(
hide-details
>
<template #label
><span>Now playing</span
><span>{{ t("rom.now-playing") }}</span
><span class="ml-2">{{
getEmojiForStatus("now_playing")
}}</span></template
@@ -101,7 +105,7 @@ watch(
hide-details
>
<template #label
><span>Hidden</span
><span>{{ t("rom.hidden") }}</span
><span class="ml-2">{{
getEmojiForStatus("hidden")
}}</span></template
@@ -115,7 +119,7 @@ watch(
no-gutters
>
<v-col cols="12" md="2">
<v-label>Rating</v-label>
<v-label>{{ t("rom.rating") }}</v-label>
</v-col>
<v-col cols="12" md="10">
<v-rating
@@ -134,10 +138,10 @@ watch(
</v-col>
</v-row>
<v-row class="d-flex align-center mt-4" no-gutters>
<v-col cols="12" md="2">
<v-label>Difficulty</v-label>
<v-col cols="auto">
<v-label>{{ t("rom.difficulty") }}</v-label>
</v-col>
<v-col cols="11" md="9">
<v-col>
<v-slider
:class="{ 'ml-4': mdAndUp }"
v-model="romUser.difficulty"
@@ -146,22 +150,22 @@ watch(
step="1"
hide-details
track-fill-color="romm-accent-1"
/>
</v-col>
<v-col cols="1">
<v-label class="ml-2 opacity-100">
{{
difficultyEmojis[Math.floor(romUser.difficulty) - 1] ??
difficultyEmojis[3]
}}
</v-label>
><template #append>
<v-label class="opacity-100">
{{
difficultyEmojis[Math.floor(romUser.difficulty) - 1] ??
difficultyEmojis[3]
}}
</v-label>
</template></v-slider
>
</v-col>
</v-row>
<v-row class="d-flex align-center mt-4" no-gutters>
<v-col cols="12" md="2">
<v-label>Completion %</v-label>
<v-col cols="auto">
<v-label>{{ t("rom.completion") }} %</v-label>
</v-col>
<v-col cols="11" md="9">
<v-col>
<v-slider
:class="{ 'ml-4': mdAndUp }"
v-model="romUser.completion"
@@ -170,20 +174,20 @@ watch(
step="1"
hide-details
track-fill-color="romm-accent-1"
/>
><template #append>
<v-label class="ml-2 opacity-100">
{{ romUser.completion }}%
</v-label>
</template></v-slider
>
</v-col>
<v-col cols="1"
><v-label class="ml-2 opacity-100">
{{ romUser.completion }}%
</v-label></v-col
>
</v-row>
<div class="d-flex align-center mt-4">
<v-select
v-model="romUser.status"
:items="statusOptions"
hide-details
label="Status"
:label="t('rom.status')"
clearable
rounded="0"
variant="outlined"
@@ -218,7 +222,7 @@ watch(
<v-card rounded="0">
<v-card-title class="bg-terciary">
<v-list-item class="pl-2 pr-0">
<span class="text-h6">My notes</span>
<span class="text-h6">{{ t("rom.my-notes") }}</span>
<template #append>
<v-btn-group divided density="compact">
<v-tooltip
@@ -284,7 +288,7 @@ watch(
<v-card rounded="0" v-if="publicNotes && publicNotes.length > 0" class="mt-2">
<v-card-title class="bg-terciary">
<v-list-item class="pl-2 pr-0">
<span class="text-h6">Public notes</span>
<span class="text-h6">{{ t("rom.public-notes") }}</span>
</v-list-item>
</v-card-title>

View File

@@ -8,8 +8,10 @@ import { formatBytes, formatTimestamp } from "@/utils";
import type { Emitter } from "mitt";
import { inject, onMounted, ref, watch } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const { xs, mdAndUp } = useDisplay();
const props = defineProps<{ rom: DetailedRom }>();
const selectedSaves = ref<SaveSchema[]>([]);
@@ -144,7 +146,7 @@ onMounted(() => {
</v-chip>
</template>
<template #no-data
><span>No saves found for {{ rom.name }}</span></template
><span>{{ t("rom.no-saves-found") }}</span></template
>
<template #item.actions="{ item }">
<v-btn-group divided density="compact">

View File

@@ -8,8 +8,10 @@ import { formatBytes, formatTimestamp } from "@/utils";
import type { Emitter } from "mitt";
import { inject, onMounted, ref, watch } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const { xs, mdAndUp } = useDisplay();
const props = defineProps<{ rom: DetailedRom }>();
const selectedStates = ref<StateSchema[]>([]);
@@ -145,7 +147,7 @@ onMounted(() => {
</v-chip>
</template>
<template #no-data
><span>No states found for {{ rom.name }}</span></template
><span>{{ t("rom.no-states-found") }}</span></template
>
<template #item.actions="{ item }">
<v-btn-group divided density="compact">

View File

@@ -1,12 +0,0 @@
<script setup lang="ts">
import DeleteBtn from "@/components/Gallery/AppBar/Collection/DeleteBtn.vue";
import EditBtn from "@/components/Gallery/AppBar/Collection/EditBtn.vue";
</script>
<template>
<v-list rounded="0" class="pa-0">
<edit-btn />
<v-divider />
<delete-btn />
</v-list>
</template>

View File

@@ -5,27 +5,14 @@ import FilterDrawer from "@/components/Gallery/AppBar/common/FilterDrawer/Base.v
import FilterTextField from "@/components/Gallery/AppBar/common/FilterTextField.vue";
import GalleryViewBtn from "@/components/Gallery/AppBar/common/GalleryViewBtn.vue";
import SelectingBtn from "@/components/Gallery/AppBar/common/SelectingBtn.vue";
import CollectionCard from "@/components/common/Collection/Card.vue";
import RSection from "@/components/common/RSection.vue";
import storeAuth from "@/stores/auth";
import storeRoms from "@/stores/roms";
import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import storeNavigation from "@/stores/navigation";
import { inject, ref } from "vue";
import { useDisplay } from "vuetify";
// Props
const { xs } = useDisplay();
const emitter = inject<Emitter<Events>>("emitter");
const viewportWidth = ref(window.innerWidth);
const auth = storeAuth();
const romsStore = storeRoms();
const { currentCollection } = storeToRefs(romsStore);
const navigationStore = storeNavigation();
const { activeCollectionInfoDrawer } = storeToRefs(navigationStore);
const open = ref(false);
</script>
<template>

View File

@@ -11,8 +11,10 @@ import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import { inject, ref } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const { xs } = useDisplay();
const emitter = inject<Emitter<Events>>("emitter");
const viewportWidth = ref(window.innerWidth);
@@ -28,11 +30,7 @@ const collectionInfoFields = [
},
{
key: "user__username",
label: "Owner",
},
{
key: "is_public",
label: "Public",
label: t("collection.owner"),
},
];
</script>
@@ -63,7 +61,17 @@ const collectionInfoFields = [
<p class="text-subtitle-2">
<span>{{ currentCollection.description }}</span>
</p>
<div class="mt-6">
<v-chip class="mt-4" size="small" color="romm-accent-1"
><v-icon class="mr-1">{{
currentCollection.is_public ? "mdi-lock-open" : "mdi-lock"
}}</v-icon
>{{
currentCollection.is_public
? t("collection.public")
: t("collection.private")
}}</v-chip
>
<div class="mt-4">
<v-btn
v-if="currentCollection.user__username === auth.user?.username"
rounded="4"
@@ -77,7 +85,7 @@ const collectionInfoFields = [
<template #prepend>
<v-icon>mdi-pencil-box</v-icon>
</template>
Edit collection
{{ t("collection.edit-collection") }}
</v-btn>
</div>
</div>
@@ -118,7 +126,7 @@ const collectionInfoFields = [
"
icon="mdi-alert"
icon-color="red"
title="Danger zone"
:title="t('collection.danger-zone')"
elevation="0"
>
<template #content>
@@ -131,7 +139,7 @@ const collectionInfoFields = [
"
>
<v-icon class="text-romm-red mr-2">mdi-delete</v-icon>
Delete collection
{{ t("collection.delete-collection") }}
</v-btn>
</div>
</template>

View File

@@ -1,31 +0,0 @@
<script setup lang="ts">
import { type Collection } from "@/stores/collections";
import storeRoms from "@/stores/roms";
import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { inject } from "vue";
import { useRoute } from "vue-router";
// Props
const emitter = inject<Emitter<Events>>("emitter");
const route = useRoute();
const roms = storeRoms();
</script>
<template>
<v-list-item
v-if="route.params.collection"
class="py-4 pr-5 text-romm-red"
@click="
emitter?.emit(
'showDeleteCollectionDialog',
roms.currentCollection as Collection,
)
"
>
<v-list-item-title class="d-flex">
<v-icon icon="mdi-delete" color="red" class="mr-2" />
Delete collection
</v-list-item-title>
</v-list-item>
</template>

View File

@@ -1,31 +0,0 @@
<script setup lang="ts">
import storeRoms from "@/stores/roms";
import type { Collection } from "@/stores/collections";
import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { inject } from "vue";
import { useRoute } from "vue-router";
// Props
const emitter = inject<Emitter<Events>>("emitter");
const route = useRoute();
const roms = storeRoms();
</script>
<template>
<v-list-item
v-if="route.params.collection"
class="py-4 pr-5"
@click="
emitter?.emit(
'showEditCollectionDialog',
roms.currentCollection as Collection,
)
"
>
<v-list-item-title class="d-flex">
<v-icon icon="mdi-pencil-box" class="mr-2" />
Edit collection
</v-list-item-title>
</v-list-item>
</template>

View File

@@ -1,14 +0,0 @@
<script setup lang="ts">
import ScanBtn from "@/components/Gallery/AppBar/Platform/ScanBtn.vue";
import UploadRomBtn from "@/components/Gallery/AppBar/Platform/UploadRomBtn.vue";
import DeleteBtn from "@/components/Gallery/AppBar/Platform/DeleteBtn.vue";
</script>
<template>
<v-list rounded="0" class="pa-0">
<upload-rom-btn />
<scan-btn />
<v-divider />
<delete-btn />
</v-list>
</template>

View File

@@ -1,31 +0,0 @@
<script setup lang="ts">
import { type Platform } from "@/stores/platforms";
import storeRoms from "@/stores/roms";
import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { inject } from "vue";
import { useRoute } from "vue-router";
// Props
const emitter = inject<Emitter<Events>>("emitter");
const roms = storeRoms();
const route = useRoute();
</script>
<template>
<v-list-item
v-if="route.params.platform"
class="py-4 pr-5 text-romm-red"
@click="
emitter?.emit(
'showDeletePlatformDialog',
roms.currentPlatform as Platform,
)
"
>
<v-list-item-title class="d-flex">
<v-icon icon="mdi-delete" color="red" class="mr-2" />
Delete platform
</v-list-item-title>
</v-list-item>
</template>

View File

@@ -1,8 +1,10 @@
<script setup lang="ts">
import storeGalleryView from "@/stores/galleryView";
import { storeToRefs } from "pinia";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const galleryViewStore = storeGalleryView();
const { activeFirmwareDrawer } = storeToRefs(galleryViewStore);
</script>
@@ -12,7 +14,7 @@ const { activeFirmwareDrawer } = storeToRefs(galleryViewStore);
location="bottom"
class="tooltip"
transition="fade-transition"
text="Show firmwares/BIOS"
:text="t('platform.show-firmwares')"
open-delay="1000"
>
<template #activator="{ props }">

View File

@@ -11,8 +11,10 @@ import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import { inject, ref, watch, onMounted } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const { xs, mdAndUp } = useDisplay();
const auth = storeAuth();
const romsStore = storeRoms();
@@ -172,9 +174,7 @@ onMounted(() => {
</v-list-item>
</template>
<template #no-data
><span
>No firmware found for {{ currentPlatform?.name }}</span
></template
><span>{{ t("platform.no-firmware-found") }}</span></template
>
<template #item.actions="{ item }">
<v-btn-group divided density="compact">

View File

@@ -14,8 +14,10 @@ import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import { computed, inject, ref, watch } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const emitter = inject<Emitter<Events>>("emitter");
const { xs } = useDisplay();
const viewportWidth = ref(window.innerWidth);
@@ -42,16 +44,16 @@ const aspectRatioOptions = computed(() => [
{
name: "1 / 1",
size: 1 / 1,
source: "Old squared cases",
source: t("platform.old-squared-cases"),
},
]);
const platformInfoFields = [
{ key: "slug", label: "Slug" },
{ key: "fs_slug", label: "Filesystem folder name" },
{ key: "category", label: "Category" },
{ key: "generation", label: "Generation" },
{ key: "family_name", label: "Family" },
{ key: "fs_slug", label: t("platform.filesystem-folder-name") },
{ key: "category", label: t("platform.category") },
{ key: "generation", label: t("platform.generation") },
{ key: "family_name", label: t("platform.family") },
];
watch(
@@ -148,7 +150,7 @@ async function setAspectRatio() {
@click="emitter?.emit('showUploadRomDialog', currentPlatform)"
>
<v-icon class="text-romm-green mr-2">mdi-upload</v-icon>
Upload roms
{{ t("platform.upload-roms") }}
</v-btn>
<v-btn
:disabled="scanning"
@@ -162,7 +164,7 @@ async function setAspectRatio() {
>mdi-magnify-scan</v-icon
>
</template>
Scan platform
{{ t("scan.scan") }}
<template #loader>
<v-progress-circular
color="romm-accent-1"
@@ -228,7 +230,7 @@ async function setAspectRatio() {
<r-section
v-if="auth.scopes.includes('platforms.write')"
icon="mdi-cog"
title="Settings"
:title="t('platform.settings')"
elevation="0"
>
<template #content>
@@ -237,7 +239,7 @@ async function setAspectRatio() {
variant="text"
class="ml-2"
prepend-icon="mdi-aspect-ratio"
>Cover style</v-chip
>{{ t("platform.cover-style") }}</v-chip
>
<v-divider class="border-opacity-25 mx-2" />
<v-item-group
@@ -285,7 +287,7 @@ async function setAspectRatio() {
v-if="auth.scopes.includes('platforms.write')"
icon="mdi-alert"
icon-color="red"
title="Danger zone"
:title="t('platform.danger-zone')"
elevation="0"
>
<template #content>
@@ -296,7 +298,7 @@ async function setAspectRatio() {
@click="emitter?.emit('showDeletePlatformDialog', currentPlatform)"
>
<v-icon class="text-romm-red mr-2">mdi-delete</v-icon>
Delete platform
{{ t("platform.delete-platform") }}
</v-btn>
</div>
</template>

View File

@@ -1,36 +0,0 @@
<script setup lang="ts">
import socket from "@/services/socket";
import storeHeartbeat from "@/stores/heartbeat";
import storeRoms from "@/stores/roms";
import storeScanning from "@/stores/scanning";
// Props
const scanningStore = storeScanning();
const romsStore = storeRoms();
const heartbeat = storeHeartbeat();
async function scan() {
scanningStore.set(true);
if (!socket.connected) socket.connect();
socket.emit("scan", {
platforms: [romsStore.currentPlatform?.id],
type: "quick",
apis: heartbeat.getMetadataOptions().map((s) => s.value),
});
}
</script>
<template>
<v-list-item
v-if="romsStore.currentPlatform?.id"
class="py-4 pr-5"
@click="scan"
>
<v-list-item-title class="d-flex">
<v-icon icon="mdi-magnify-scan" class="mr-2" />
Scan platform
</v-list-item-title>
</v-list-item>
</template>

View File

@@ -1,29 +0,0 @@
<script setup lang="ts">
import { inject } from "vue";
import { useRoute } from "vue-router";
import type { Emitter } from "mitt";
import type { Events } from "@/types/emitter";
import storePlatforms, { type Platform } from "@/stores/platforms";
// Props
const emitter = inject<Emitter<Events>>("emitter");
const platforms = storePlatforms();
const route = useRoute();
</script>
<template>
<v-list-item
v-if="route.params.platform"
class="py-4 pr-5"
@click="
emitter?.emit(
'showUploadRomDialog',
platforms.get(Number(route.params.platform)) as Platform,
)
"
>
<v-list-item-title class="d-flex">
<v-icon icon="mdi-upload" class="mr-2" />Upload roms
</v-list-item-title>
</v-list-item>
</template>

View File

@@ -1,8 +1,10 @@
<script setup lang="ts">
import storeGalleryFilter from "@/stores/galleryFilter";
import { storeToRefs } from "pinia";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const galleryFilterStore = storeGalleryFilter();
const { activeFilterDrawer } = storeToRefs(galleryFilterStore);
</script>
@@ -12,7 +14,7 @@ const { activeFilterDrawer } = storeToRefs(galleryFilterStore);
location="bottom"
class="tooltip"
transition="fade-transition"
text="Filter gallery"
:text="t('platform.filter-gallery')"
open-delay="1000"
>
<template #activator="{ props }">

View File

@@ -9,11 +9,13 @@ import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import { inject, nextTick } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const { xs } = useDisplay();
const emitter = inject<Emitter<Events>>("emitter");
const galleryFilterStore = storeGalleryFilter();
const {
activeFilterDrawer,
selectedGenre,
@@ -29,40 +31,40 @@ const {
selectedStatus,
filterStatuses,
} = storeToRefs(galleryFilterStore);
const filters = [
{
label: "Genre",
label: t("platform.genre"),
selected: selectedGenre,
items: filterGenres,
},
{
label: "Franchise",
label: t("platform.franchise"),
selected: selectedFranchise,
items: filterFranchises,
},
{
label: "Collection",
label: t("platform.collection"),
selected: selectedCollection,
items: filterCollections,
},
{
label: "Company",
label: t("platform.company"),
selected: selectedCompany,
items: filterCompanies,
},
{
label: "Age Rating",
label: t("platform.age-rating"),
selected: selectedAgeRating,
items: filterAgeRatings,
},
{
label: "Status",
label: t("platform.status"),
selected: selectedStatus,
items: filterStatuses,
},
];
// Functions
function resetFilters() {
selectedGenre.value = null;
selectedFranchise.value = null;
@@ -108,7 +110,7 @@ function resetFilters() {
</v-list-item>
<v-list-item class="justify-center d-flex">
<v-btn size="small" variant="tonal" @click="resetFilters">
Reset filters
{{ t("platform.reset-filters") }}
</v-btn>
</v-list-item>
</v-list>

View File

@@ -4,8 +4,10 @@ import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import { inject } from "vue";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const galleryFilterStore = storeGalleryFilter();
const { filterDuplicates } = storeToRefs(galleryFilterStore);
const emitter = inject<Emitter<Events>>("emitter");
@@ -31,7 +33,7 @@ function setDuplicates() {
'text-romm-white': !filterDuplicates,
'text-romm-accent-1': filterDuplicates,
}"
>Show duplicates</span
>{{ t("platform.show-duplicates") }}</span
></v-btn
>
</template>

View File

@@ -4,8 +4,10 @@ import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import { inject } from "vue";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const galleryFilterStore = storeGalleryFilter();
const { filterFavourites } = storeToRefs(galleryFilterStore);
const emitter = inject<Emitter<Events>>("emitter");
@@ -31,7 +33,7 @@ function setFavourites() {
'text-romm-white': !filterFavourites,
'text-romm-accent-1': filterFavourites,
}"
>Show favourites</span
>{{ t("platform.show-favourites") }}</span
></v-btn
>
</template>

View File

@@ -4,8 +4,10 @@ import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import { inject } from "vue";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const galleryFilterStore = storeGalleryFilter();
const { filterUnmatched } = storeToRefs(galleryFilterStore);
const emitter = inject<Emitter<Events>>("emitter");
@@ -31,7 +33,7 @@ function setUnmatched() {
'text-romm-white': !filterUnmatched,
'text-romm-accent-1': filterUnmatched,
}"
>Show unmatched</span
>{{ t("platform.show-unmatched") }}</span
></v-btn
>
</template>

View File

@@ -5,8 +5,10 @@ import { debounce } from "lodash";
import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import { inject, nextTick } from "vue";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const emitter = inject<Emitter<Events>>("emitter");
const galleryFilterStore = storeGalleryFilter();
const { filterSearch } = storeToRefs(galleryFilterStore);
@@ -24,7 +26,7 @@ function clear() {
<v-text-field
v-model="filterSearch"
prepend-inner-icon="mdi-filter-outline"
label="Filter"
:label="t('common.filter')"
rounded="0"
hide-details
clearable

View File

@@ -1,8 +1,10 @@
<script setup lang="ts">
import storeGalleryView from "@/stores/galleryView";
import { views } from "@/utils";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const galleryView = storeGalleryView();
</script>
@@ -11,7 +13,7 @@ const galleryView = storeGalleryView();
location="bottom"
class="tooltip"
transition="fade-transition"
text="Change view"
:text="t('platform.change-view')"
open-delay="1000"
>
<template #activator="{ props }">

View File

@@ -1,7 +1,9 @@
<script setup lang="ts">
import storeRoms from "@/stores/roms";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const romsStore = storeRoms();
</script>
@@ -10,7 +12,7 @@ const romsStore = storeRoms();
location="bottom"
class="tooltip"
transition="fade-transition"
text="Active multi-select"
:text="t('platform.active-multi-select')"
open-delay="1000"
>
<template #activator="{ props }">

View File

@@ -4,15 +4,17 @@ import RSection from "@/components/common/RSection.vue";
import storeCollections from "@/stores/collections";
import { views } from "@/utils";
import { isNull } from "lodash";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const collections = storeCollections();
const gridCollections = isNull(localStorage.getItem("settings.gridCollections"))
? true
: localStorage.getItem("settings.gridCollections") === "true";
</script>
<template>
<r-section icon="mdi-bookmark-box-multiple" title="Collections">
<r-section icon="mdi-bookmark-box-multiple" :title="t('common.collections')">
<template #content>
<v-row
:class="{ 'flex-nowrap overflow-x-auto': !gridCollections }"

View File

@@ -4,15 +4,17 @@ import RSection from "@/components/common/RSection.vue";
import storePlatforms from "@/stores/platforms";
import { isNull } from "lodash";
import { views } from "@/utils";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const platforms = storePlatforms();
const gridPlatforms = isNull(localStorage.getItem("settings.gridPlatforms"))
? true
: localStorage.getItem("settings.gridPlatforms") === "true";
</script>
<template>
<r-section icon="mdi-controller" title="Platforms">
<r-section icon="mdi-controller" :title="t('common.platforms')">
<template #content>
<v-row
:class="{ 'flex-nowrap overflow-x-auto': !gridPlatforms }"

View File

@@ -6,8 +6,10 @@ import { views } from "@/utils";
import { storeToRefs } from "pinia";
import { useRouter } from "vue-router";
import { isNull } from "lodash";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const romsStore = storeRoms();
const { recentRoms } = storeToRefs(romsStore);
const router = useRouter();
@@ -16,7 +18,7 @@ const gridRecentRoms = isNull(localStorage.getItem("settings.gridRecentRoms"))
: localStorage.getItem("settings.gridRecentRoms") === "true";
</script>
<template>
<r-section icon="mdi-shimmer" title="Recently added">
<r-section icon="mdi-shimmer" :title="t('home.recently-added')">
<template #content>
<v-row
:class="{ 'flex-nowrap overflow-x-auto': !gridRecentRoms }"
@@ -46,8 +48,6 @@ const gridRecentRoms = isNull(localStorage.getItem("settings.gridRecentRoms"))
/>
</v-col>
</v-row>
<!-- TODO: Check recently added games in the last 30 days -->
<!-- TODO: Add a button to upload roms if no roms were uploaded in the last 30 days -->
</template>
</r-section>
</template>

View File

@@ -1,7 +1,10 @@
<script setup lang="ts">
import api from "@/services/api/index";
import { onBeforeMount, ref } from "vue";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const stats = ref({
PLATFORMS: 0,
ROMS: 0,
@@ -11,6 +14,7 @@ const stats = ref({
FILESIZE: 0,
});
// Functions
onBeforeMount(() => {
api.get("/stats").then(({ data }) => {
stats.value = data;
@@ -29,7 +33,7 @@ onBeforeMount(() => {
variant="text"
label
>
{{ stats.PLATFORMS }} Platforms
{{ stats.PLATFORMS }} {{ t("common.platforms") }}
</v-chip>
</v-col>
<v-col>
@@ -39,7 +43,7 @@ onBeforeMount(() => {
variant="text"
label
>
{{ stats.ROMS }} Games
{{ stats.ROMS }} {{ t("common.games") }}
</v-chip>
</v-col>
<v-col>
@@ -49,7 +53,7 @@ onBeforeMount(() => {
variant="text"
label
>
{{ stats.SAVES }} Saves
{{ stats.SAVES }} {{ t("common.saves") }}
</v-chip>
</v-col>
<v-col>
@@ -59,7 +63,7 @@ onBeforeMount(() => {
variant="text"
label
>
{{ stats.STATES }} States
{{ stats.STATES }} {{ t("common.states") }}
</v-chip>
</v-col>
<v-col>
@@ -69,7 +73,7 @@ onBeforeMount(() => {
variant="text"
label
>
{{ stats.SCREENSHOTS }} Screenshots
{{ stats.SCREENSHOTS }} {{ t("common.screenshots") }}
</v-chip>
</v-col>
</v-row>

View File

@@ -9,8 +9,10 @@ import { defaultAvatarPath } from "@/utils";
import type { Emitter } from "mitt";
import { inject, ref } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const user = ref<UserItem | null>(null);
const { lgAndUp } = useDisplay();
const show = ref(false);
@@ -95,7 +97,7 @@ function closeDialog() {
v-model="user.username"
rounded="0"
variant="outlined"
label="username"
:label="t('settings.username')"
required
hide-details
clearable
@@ -108,7 +110,7 @@ function closeDialog() {
v-model="user.password"
rounded="0"
variant="outlined"
label="Password"
:label="t('settings.password')"
required
hide-details
clearable
@@ -135,7 +137,7 @@ function closeDialog() {
rounded="0"
variant="outlined"
:items="['viewer', 'editor', 'admin']"
label="Role"
:label="t('settings.role')"
required
hide-details
/>
@@ -184,14 +186,16 @@ function closeDialog() {
<template #append>
<v-row class="justify-center mb-2" no-gutters>
<v-btn-group divided density="compact">
<v-btn class="bg-terciary" @click="closeDialog"> Cancel </v-btn>
<v-btn class="bg-terciary" @click="closeDialog">
{{ t("common.cancel") }}
</v-btn>
<v-btn
:variant="!user.username ? 'plain' : 'flat'"
:disabled="!user.username"
class="text-romm-green bg-terciary"
@click="editUser"
>
Apply
{{ t("common.apply") }}
</v-btn>
</v-btn-group>
</v-row>

View File

@@ -1,6 +1,9 @@
<script setup lang="ts">
import storeHeartbeat from "@/stores/heartbeat";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const heartbeatStore = storeHeartbeat();
</script>
<template>
@@ -40,7 +43,7 @@ const heartbeatStore = storeHeartbeat();
style="text-decoration: none"
href="https://discord.com/invite/P5HtHnhUDH"
target="_blank"
>Join to our Discord</a
>{{ t("settings.join-discord") }}</a
></span
>
</v-hover>

View File

@@ -1,10 +1,14 @@
<script setup lang="ts">
import { useI18n } from "vue-i18n";
// Props
withDefaults(
defineProps<{
enabled?: boolean;
}>(),
{ enabled: false },
);
const { t } = useI18n();
const emit = defineEmits(["click"]);
</script>
<template>
@@ -19,7 +23,7 @@ const emit = defineEmits(["click"]);
class="text-romm-accent-1"
@click="$emit('click')"
>
Add
{{ t("common.add") }}
</v-btn>
</v-expand-transition>
</template>

View File

@@ -6,8 +6,10 @@ import type { Emitter } from "mitt";
import storeConfig from "@/stores/config";
import { inject, ref } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const { mdAndUp, smAndDown } = useDisplay();
const show = ref(false);
const emitter = inject<Emitter<Events>>("emitter");
@@ -81,14 +83,16 @@ function closeDialog() {
<template #append>
<v-row class="justify-center mb-2" no-gutters>
<v-btn-group divided density="compact">
<v-btn class="bg-terciary" @click="closeDialog"> Cancel </v-btn>
<v-btn class="bg-terciary" @click="closeDialog">
{{ t("common.cancel") }}
</v-btn>
<v-btn
class="bg-terciary text-romm-green"
:disabled="exclusionValue == ''"
:variant="exclusionValue == '' ? 'plain' : 'flat'"
@click="addExclusion"
>
Confirm
{{ t("common.confirm") }}
</v-btn>
</v-btn-group>
</v-row>

View File

@@ -10,8 +10,10 @@ import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { inject, ref } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const { mdAndUp } = useDisplay();
const show = ref(false);
const configStore = storeConfig();
@@ -100,7 +102,7 @@ function closeDialog() {
<v-select
:items="heartbeat.value.FS_PLATFORMS"
v-model="fsSlugToCreate"
label="Folder name"
:label="t('settings.folder-name')"
variant="outlined"
required
hide-details
@@ -114,7 +116,7 @@ function closeDialog() {
<v-autocomplete
v-model="selectedPlatform"
class="text-romm-accent-1"
label="RomM platform"
:label="t('settings.romm-platform')"
:items="supportedPlatforms"
color="romm-accent-1"
base-color="romm-accent-1"
@@ -159,7 +161,9 @@ function closeDialog() {
<template #append>
<v-row class="justify-center mb-2" no-gutters>
<v-btn-group divided density="compact">
<v-btn class="bg-terciary" @click="closeDialog"> Cancel </v-btn>
<v-btn class="bg-terciary" @click="closeDialog">
{{ t("common.cancel") }}
</v-btn>
<v-btn
class="bg-terciary text-romm-green"
:disabled="fsSlugToCreate == '' || selectedPlatform?.slug == ''"
@@ -170,7 +174,7 @@ function closeDialog() {
"
@click="addBindPlatform"
>
Confirm
{{ t("common.confirm") }}
</v-btn>
</v-btn-group>
</v-row>

View File

@@ -10,8 +10,10 @@ import storeHeartbeat from "@/stores/heartbeat";
import type { Emitter } from "mitt";
import { inject, ref } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const { mdAndUp } = useDisplay();
const show = ref(false);
const configStore = storeConfig();
@@ -101,7 +103,7 @@ function closeDialog() {
<v-select
:items="heartbeat.value.FS_PLATFORMS"
v-model="fsSlugToCreate"
label="Platform version"
:label="t('settings.platform-version')"
variant="outlined"
required
hide-details
@@ -115,7 +117,7 @@ function closeDialog() {
<v-autocomplete
v-model="selectedPlatform"
class="text-romm-accent-1"
label="Main platform"
:label="t('settings.main-platform')"
color="romm-accent-1"
:items="supportedPlatforms"
base-color="romm-accent-1"
@@ -160,7 +162,9 @@ function closeDialog() {
<template #append>
<v-row class="justify-center mb-2" no-gutters>
<v-btn-group divided density="compact">
<v-btn class="bg-terciary" @click="closeDialog"> Cancel </v-btn>
<v-btn class="bg-terciary" @click="closeDialog">
{{ t("common.cancel") }}
</v-btn>
<v-btn
class="bg-terciary text-romm-green"
:disabled="fsSlugToCreate == '' || selectedPlatform?.slug == ''"
@@ -171,7 +175,7 @@ function closeDialog() {
"
@click="addVersionPlatform"
>
Confirm
{{ t("common.confirm") }}
</v-btn>
</v-btn-group>
</v-row>

View File

@@ -5,44 +5,46 @@ import ExcludedCard from "@/components/Settings/LibraryManagement/ExcludedCard.v
import storeAuth from "@/stores/auth";
import storeConfig from "@/stores/config";
import { ref } from "vue";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const configStore = storeConfig();
const authStore = storeAuth();
const exclusions = [
{
set: configStore.config.EXCLUDED_PLATFORMS,
title: "Platform",
title: t("common.platform"),
icon: "mdi-controller-off",
type: "EXCLUDED_PLATFORMS",
},
{
set: configStore.config.EXCLUDED_SINGLE_FILES,
title: "Single rom files",
title: t("settings.excluded-single-rom-files"),
icon: "mdi-file-document-remove-outline",
type: "EXCLUDED_SINGLE_FILES",
},
{
set: configStore.config.EXCLUDED_SINGLE_EXT,
title: "Single Roms Extensions",
title: t("settings.excluded-single-rom-extensions"),
icon: "mdi-file-document-remove-outline",
type: "EXCLUDED_SINGLE_EXT",
},
{
set: configStore.config.EXCLUDED_MULTI_FILES,
title: "Multi Roms Files",
title: t("settings.excluded-multi-rom-files"),
icon: "mdi-file-document-remove-outline",
type: "EXCLUDED_MULTI_FILES",
},
{
set: configStore.config.EXCLUDED_MULTI_PARTS_FILES,
title: "Multi Roms Parts Files",
title: t("settings.excluded-multi-rom-parts-files"),
icon: "mdi-file-document-remove-outline",
type: "EXCLUDED_MULTI_PARTS_FILES",
},
{
set: configStore.config.EXCLUDED_MULTI_PARTS_EXT,
title: "Multi Roms Parts Extensions",
title: t("settings.excluded-multi-rom-parts-extensions"),
icon: "mdi-file-document-remove-outline",
type: "EXCLUDED_MULTI_PARTS_EXT",
},
@@ -50,7 +52,7 @@ const exclusions = [
const editable = ref(false);
</script>
<template>
<r-section icon="mdi-cancel" title="Excluded">
<r-section icon="mdi-cancel" :title="t('settings.excluded')">
<template #toolbar-append>
<v-btn
v-if="authStore.scopes.includes('platforms.write')"

View File

@@ -4,8 +4,10 @@ import storeConfig from "@/stores/config";
import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { inject } from "vue";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const emitter = inject<Emitter<Events>>("emitter");
const props = defineProps<{
set: string[];
@@ -71,7 +73,7 @@ function removeExclusion(exclusionValue: string) {
})
"
>
Add
{{ t("common.add") }}
</v-btn>
</v-expand-transition>
</v-card-text>

View File

@@ -10,8 +10,10 @@ import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import { inject, ref } from "vue";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const emitter = inject<Emitter<Events>>("emitter");
const authStore = storeAuth();
const configStore = storeConfig();
@@ -20,7 +22,7 @@ const editable = ref(false);
</script>
<template>
<r-section icon="mdi-controller" title="Platforms Bindings">
<r-section icon="mdi-controller" :title="t('settings.platforms-bindings')">
<template #toolbar-append>
<v-btn
v-if="authStore.scopes.includes('platforms.write')"

View File

@@ -10,8 +10,10 @@ import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import { inject, ref } from "vue";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const emitter = inject<Emitter<Events>>("emitter");
const authStore = storeAuth();
const configStore = storeConfig();
@@ -20,7 +22,10 @@ const editable = ref(false);
</script>
<template>
<r-section icon="mdi-gamepad-variant" title="Platforms Versions">
<r-section
icon="mdi-gamepad-variant"
:title="t('settings.platforms-versions')"
>
<template #toolbar-append>
<v-btn
v-if="authStore.scopes.includes('platforms.write')"

View File

@@ -3,7 +3,10 @@ import InterfaceOption from "@/components/Settings/UserInterface/InterfaceOption
import RSection from "@/components/common/RSection.vue";
import { computed, ref } from "vue";
import { isNull } from "lodash";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
// Initializing refs from localStorage
const storedShowRecentRoms = localStorage.getItem("settings.showRecentRoms");
const showRecentRomsRef = ref(
@@ -49,16 +52,16 @@ const statusRef = ref(isNull(storedStatus) ? true : storedStatus === "true");
const homeOptions = computed(() => [
{
title: "Show recently added roms",
description: "Show recently added roms section at the home page",
title: t("settings.show-recently-added"),
description: t("settings.show-recently-added-desc"),
iconEnabled: "mdi-checkbox-marked-outline",
iconDisabled: "mdi-checkbox-blank-outline",
model: showRecentRomsRef,
modelTrigger: toggleShowRecentRoms,
},
{
title: "Recently added roms as grid",
description: "View recently added rom cards as a grid at the home page",
title: t("settings.recently-added-as-grid"),
description: t("settings.recently-added-as-grid-desc"),
iconEnabled: "mdi-view-comfy",
iconDisabled: "mdi-view-column",
disabled: !showRecentRomsRef.value,
@@ -66,16 +69,16 @@ const homeOptions = computed(() => [
modelTrigger: toggleGridRecentRoms,
},
{
title: "Show platforms",
description: "Show platform section at the home page",
title: t("settings.show-platforms"),
description: t("settings.show-platforms-desc"),
iconEnabled: "mdi-checkbox-marked-outline",
iconDisabled: "mdi-checkbox-blank-outline",
model: showPlatformsRef,
modelTrigger: toggleShowPlatforms,
},
{
title: "Platforms as grid",
description: "View platform cards as a grid at the home page",
title: t("settings.show-platforms-as-grid"),
description: t("settings.show-platforms-as-grid-desc"),
iconEnabled: "mdi-view-comfy",
iconDisabled: "mdi-view-column",
disabled: !showPlatformsRef.value,
@@ -83,16 +86,16 @@ const homeOptions = computed(() => [
modelTrigger: toggleGridPlatforms,
},
{
title: "Show collections",
description: "Show collections section at the home page",
title: t("settings.show-collections"),
description: t("settings.show-collections-desc"),
iconEnabled: "mdi-checkbox-marked-outline",
iconDisabled: "mdi-checkbox-blank-outline",
model: showCollectionsRef,
modelTrigger: toggleShowCollections,
},
{
title: "Collections a grid",
description: "View collection cards as a grid at the home page",
title: t("settings.show-collections-as-grid"),
description: t("settings.show-collections-as-grid-desc"),
iconEnabled: "mdi-view-comfy",
iconDisabled: "mdi-view-column",
disabled: !showCollectionsRef.value,
@@ -103,17 +106,16 @@ const homeOptions = computed(() => [
const galleryOptions = computed(() => [
{
title: "Group roms",
description: "Group versions of the same rom together in the gallery",
title: t("settings.group-roms"),
description: t("settings.group-roms-desc"),
iconEnabled: "mdi-group",
iconDisabled: "mdi-ungroup",
model: groupRomsRef,
modelTrigger: toggleGroupRoms,
},
{
title: "Show siblings",
description:
'Show siblings count in the gallery when "Group roms" option is enabled',
title: t("settings.show-siblings"),
description: t("settings.show-siblings-desc"),
iconEnabled: "mdi-account-group-outline",
iconDisabled: "mdi-account-outline",
model: siblingsRef,
@@ -121,25 +123,24 @@ const galleryOptions = computed(() => [
modelTrigger: toggleSiblings,
},
{
title: "Show regions",
description: "Show region flags in the gallery",
title: t("settings.show-regions"),
description: t("settings.show-regions-desc"),
iconEnabled: "mdi-flag-outline",
iconDisabled: "mdi-flag-off-outline",
model: regionsRef,
modelTrigger: toggleRegions,
},
{
title: "Show languages",
description: "Show language flags in the gallery",
title: t("settings.show-languages"),
description: t("settings.show-languages-desc"),
iconEnabled: "mdi-flag-outline",
iconDisabled: "mdi-flag-off-outline",
model: languagesRef,
modelTrigger: toggleLanguages,
},
{
title: "Show status",
description:
"Show status icons in the gallery (backlogged, playing, completed, etc)",
title: t("settings.show-status"),
description: t("settings.show-status-desc"),
iconEnabled: "mdi-check-circle-outline",
iconDisabled: "mdi-close-circle-outline",
model: statusRef,
@@ -199,11 +200,11 @@ const toggleStatus = (value: boolean) => {
};
</script>
<template>
<r-section icon="mdi-palette-swatch-outline" title="Interface">
<r-section icon="mdi-palette-swatch-outline" :title="t('settings.interface')">
<template #content>
<v-chip label variant="text" prepend-icon="mdi-home" class="ml-2"
>Home</v-chip
>
<v-chip label variant="text" prepend-icon="mdi-home" class="ml-2">{{
t("settings.home")
}}</v-chip>
<v-divider class="border-opacity-25 mx-2" />
<v-row class="py-1" no-gutters>
<v-col
@@ -230,7 +231,7 @@ const toggleStatus = (value: boolean) => {
variant="text"
prepend-icon="mdi-view-grid"
class="ml-2 mt-4"
>Gallery</v-chip
>{{ t("settings.gallery") }}</v-chip
>
<v-divider class="border-opacity-25 mx-2" />
<v-row class="py-1" no-gutters>

View File

@@ -0,0 +1,15 @@
<script setup lang="ts">
import RSection from "@/components/common/RSection.vue";
import LanguageSelector from "@/components/Settings/UserInterface/LanguageSelector.vue";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
</script>
<template>
<r-section icon="mdi-translate" :title="t('settings.language')">
<template #content>
<language-selector />
</template>
</r-section>
</template>

View File

@@ -0,0 +1,31 @@
<script setup lang="ts">
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import languageStore from "@/stores/language";
import { storeToRefs } from "pinia";
// Props
const { locale } = useI18n();
const storeLanguage = languageStore();
const { languages, selectedLanguage } = storeToRefs(storeLanguage);
// Functions
function changeLanguage() {
locale.value = selectedLanguage.value.value;
localStorage.setItem("settings.locale", selectedLanguage.value.value);
}
</script>
<template>
<v-autocomplete
v-model="selectedLanguage"
:items="languages"
variant="outlined"
class="ma-2"
item-value="value"
item-title="name"
return-object
hide-details
clearable
@update:model-value="changeLanguage"
/>
</template>

View File

@@ -5,8 +5,10 @@ import { autoThemeKey, themes } from "@/styles/themes";
import { isKeyof } from "@/types";
import { computed, ref } from "vue";
import { useTheme } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const theme = useTheme();
const storedTheme = parseInt(localStorage.getItem("settings.theme") ?? "");
const selectedTheme = ref(isNaN(storedTheme) ? autoThemeKey : storedTheme);
@@ -36,7 +38,7 @@ function toggleTheme() {
}
</script>
<template>
<r-section icon="mdi-brush-variant" title="Theme">
<r-section icon="mdi-brush-variant" :title="t('settings.theme')">
<template #content>
<v-item-group
v-model="selectedTheme"

View File

@@ -1,6 +1,9 @@
<script setup lang="ts">
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const { xs } = useDisplay();
withDefaults(defineProps<{ text: string; icon: string }>(), {
text: "",
@@ -20,7 +23,7 @@ withDefaults(defineProps<{ text: string; icon: string }>(), {
<v-icon>
{{ icon }}
</v-icon>
<span class="ml-2" v-if="!xs">{{ text }}</span>
<span class="ml-2" v-if="!xs">{{ t(`settings.theme-${text}`) }}</span>
</div>
</v-card>
</v-item>

View File

@@ -11,8 +11,10 @@ import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { inject, ref, watch } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const { mdAndUp } = useDisplay();
const show = ref(false);
const romsStore = storeRoms();
@@ -92,9 +94,9 @@ function closeDialog() {
>
<template #header>
<v-row no-gutters class="justify-center">
<span>Adding</span>
<span>{{ t("rom.adding-to-collection-part1") }}</span>
<span class="text-romm-accent-1 mx-1">{{ roms.length }}</span>
<span>games to collection</span>
<span>{{ t("rom.adding-to-collection-part2") }}</span>
</v-row>
</template>
<template #prepend>
@@ -102,7 +104,7 @@ function closeDialog() {
v-model="selectedCollection"
class="pa-3"
density="default"
label="Collection"
:label="t('common.collection')"
item-title="name"
:items="collectionsStore.allCollections"
variant="outlined"
@@ -174,7 +176,7 @@ function closeDialog() {
<v-row class="justify-center my-2">
<v-btn-group divided density="compact">
<v-btn class="bg-terciary" @click="closeDialog" variant="flat">
Cancel
{{ t("common.cancel") }}
</v-btn>
<v-btn
class="bg-terciary text-romm-green"
@@ -182,7 +184,7 @@ function closeDialog() {
:variant="!selectedCollection ? 'plain' : 'flat'"
@click="addRomsToCollection"
>
Confirm
{{ t("common.confirm") }}
</v-btn>
</v-btn-group>
</v-row>

View File

@@ -10,8 +10,10 @@ import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { inject, ref } from "vue";
import { useDisplay, useTheme } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const theme = useTheme();
const { mdAndUp } = useDisplay();
const show = ref(false);
@@ -117,7 +119,7 @@ function closeDialog() {
<v-col>
<v-text-field
v-model="collection.name"
label="Name"
:label="t('collection.name')"
variant="outlined"
required
hide-details
@@ -130,7 +132,7 @@ function closeDialog() {
<v-text-field
v-model="collection.description"
class="mt-1"
label="Description"
:label="t('collection.description')"
variant="outlined"
required
hide-details
@@ -201,14 +203,16 @@ function closeDialog() {
<template #append>
<v-row class="justify-center mt-4 mb-2" no-gutters>
<v-btn-group divided density="compact">
<v-btn class="bg-terciary" @click="closeDialog"> Cancel </v-btn>
<v-btn class="bg-terciary" @click="closeDialog">
{{ t("common.cancel") }}
</v-btn>
<v-btn
class="bg-terciary text-romm-green"
:disabled="!collection.name"
:variant="!collection.name ? 'plain' : 'flat'"
@click="createCollection"
>
Create
{{ t("common.create") }}
</v-btn>
</v-btn-group>
</v-row>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import RAvatar from "@/components/common/Collection/RAvatar.vue";
import RAvatarCollection from "@/components/common/Collection/RAvatar.vue";
import RDialog from "@/components/common/RDialog.vue";
import collectionApi from "@/services/api/collection";
import storeCollections, { type Collection } from "@/stores/collections";
@@ -8,7 +8,10 @@ import type { Emitter } from "mitt";
import { inject, ref } from "vue";
import { useRouter } from "vue-router";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const router = useRouter();
const { lgAndUp } = useDisplay();
const collectionsStore = storeCollections();
@@ -20,6 +23,7 @@ emitter?.on("showDeleteCollectionDialog", (collectionToDelete) => {
show.value = true;
});
// Functions
async function deleteCollection() {
if (!collection.value) return;
@@ -69,18 +73,26 @@ function closeDialog() {
>
<template #content>
<v-row class="justify-center align-center pa-2" no-gutters>
<span>Removing collection</span>
<r-avatar class="ml-1" :collection="collection" />
<span class="ml-1 text-romm-accent-1">{{ collection.name }}.</span>
<span class="ml-1">from RomM. Do you confirm?</span>
<span>{{ t("collection.removing-collection-1") }}</span>
<v-chip class="pl-0 ml-1" label>
<r-avatar-collection
:collection="collection"
:size="35"
class="mr-2"
/>
{{ collection.name }}
</v-chip>
<span class="ml-1">{{ t("collection.removing-collection-2") }}</span>
</v-row>
</template>
<template #append>
<v-row class="justify-center pa-2" no-gutters>
<v-btn-group divided density="compact">
<v-btn class="bg-terciary" @click="closeDialog"> Cancel </v-btn>
<v-btn class="bg-terciary" @click="closeDialog">
{{ t("common.cancel") }}
</v-btn>
<v-btn class="bg-terciary text-romm-red" @click="deleteCollection">
Confirm
{{ t("common.confirm") }}
</v-btn>
</v-btn-group>
</v-row>

View File

@@ -9,8 +9,10 @@ import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { inject, ref } from "vue";
import { useDisplay, useTheme } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const theme = useTheme();
const { smAndDown, mdAndUp, lgAndUp } = useDisplay();
const show = ref(false);
@@ -116,7 +118,7 @@ function closeDialog() {
<v-text-field
v-model="collection.name"
class="py-2"
label="Name"
:label="t('collection.name')"
variant="outlined"
required
hide-details
@@ -129,7 +131,7 @@ function closeDialog() {
<v-text-field
v-model="collection.description"
class="py-2"
label="Description"
:label="t('collection.description')"
variant="outlined"
required
hide-details
@@ -147,11 +149,10 @@ function closeDialog() {
true-icon="mdi-lock-open"
inset
hide-details
message="Public (visible to everyone)"
:label="
collection.is_public
? 'Public (visible to everyone)'
: 'Private (only visible to me)'
? t('collection.public-desc')
: t('collection.private-desc')
"
/>
</v-col>
@@ -221,9 +222,11 @@ function closeDialog() {
</v-row>
<v-row class="justify-center pa-2 mt-1" no-gutters>
<v-btn-group divided density="compact">
<v-btn class="bg-terciary" @click="closeDialog"> Cancel </v-btn>
<v-btn class="bg-terciary" @click="closeDialog">
{{ t("common.cancel") }}
</v-btn>
<v-btn class="text-romm-green bg-terciary" @click="editCollection">
Update
{{ t("common.update") }}
</v-btn>
</v-btn-group>
</v-row>

View File

@@ -9,8 +9,10 @@ import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { inject, ref, watch } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const { mdAndUp } = useDisplay();
const show = ref(false);
const romsStore = storeRoms();
@@ -97,13 +99,9 @@ function closeDialog() {
>
<template #header>
<v-row no-gutters class="justify-center">
<span>Removing</span>
<span>{{ t("rom.remove-from-collection-part1") }}</span>
<span class="text-romm-accent-1 mx-1">{{ roms.length }}</span>
<span>games from</span>
<span class="text-romm-accent-1 mx-1">{{
selectedCollection?.name
}}</span>
<span>collection.</span>
<span>{{ t("rom.remove-from-collection-part2") }}</span>
</v-row>
</template>
<template #content>
@@ -151,14 +149,14 @@ function closeDialog() {
<v-row class="justify-center my-2">
<v-btn-group divided density="compact">
<v-btn class="bg-terciary" @click="closeDialog" variant="flat">
Cancel
{{ t("common.cancel") }}
</v-btn>
<v-btn
class="bg-terciary text-romm-red"
variant="flat"
@click="removeRomsFromCollection"
>
Confirm
{{ t("common.confirm") }}
</v-btn>
</v-btn-group>
</v-row>

View File

@@ -11,8 +11,10 @@ import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import { inject } from "vue";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const props = defineProps<{ rom: SimpleRom }>();
const emitter = inject<Emitter<Events>>("emitter");
const heartbeat = storeHeartbeat();
@@ -95,12 +97,14 @@ async function switchFromFavourites() {
@click="emitter?.emit('showMatchRomDialog', rom)"
>
<v-list-item-title class="d-flex">
<v-icon icon="mdi-search-web" class="mr-2" />Manual match
<v-icon icon="mdi-search-web" class="mr-2" />{{
t("rom.manual-match")
}}
</v-list-item-title>
<v-list-item-subtitle>
{{
!heartbeat.value.ANY_SOURCE_ENABLED
? "No metadata source enabled"
? t("rom.no-metadata-source")
: ""
}}
</v-list-item-subtitle>
@@ -110,7 +114,7 @@ async function switchFromFavourites() {
@click="emitter?.emit('showEditRomDialog', { ...rom })"
>
<v-list-item-title class="d-flex">
<v-icon icon="mdi-pencil-box" class="mr-2" />Edit
<v-icon icon="mdi-pencil-box" class="mr-2" />{{ t("rom.edit-rom") }}
</v-list-item-title>
</v-list-item>
<v-divider />
@@ -125,7 +129,9 @@ async function switchFromFavourites() {
:icon="collectionsStore.isFav(rom) ? 'mdi-star-outline' : 'mdi-star'"
class="mr-2"
/>{{
`${collectionsStore.isFav(rom) ? "Remove from" : "Add to"} favourites`
collectionsStore.isFav(rom)
? t("rom.remove-from-fav")
: t("rom.add-to-fav")
}}
</v-list-item-title>
</v-list-item>
@@ -135,8 +141,9 @@ async function switchFromFavourites() {
@click="emitter?.emit('showAddToCollectionDialog', [{ ...rom }])"
>
<v-list-item-title class="d-flex">
<v-icon icon="mdi-bookmark-plus-outline" class="mr-2" />Add to
collection
<v-icon icon="mdi-bookmark-plus-outline" class="mr-2" />{{
t("rom.add-to-collection")
}}
</v-list-item-title>
</v-list-item>
<template v-if="auth.scopes.includes('roms.write')">
@@ -146,7 +153,7 @@ async function switchFromFavourites() {
@click="emitter?.emit('showDeleteRomDialog', [rom])"
>
<v-list-item-title class="d-flex">
<v-icon icon="mdi-delete" class="mr-2" />Delete
<v-icon icon="mdi-delete" class="mr-2" />{{ t("rom.delete-rom") }}
</v-list-item-title>
</v-list-item>
</template>

View File

@@ -4,8 +4,10 @@ import type { Events } from "@/types/emitter";
import type { Emitter } from "mitt";
import { inject, ref } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const { lgAndUp } = useDisplay();
const show = ref(false);
const link = ref("");
@@ -30,9 +32,7 @@ function closeDialog() {
>
<template #content>
<v-row class="justify-center text-center pa-2" no-gutters>
<v-list-item
>Can't copy link to clipboard, copy it manually:</v-list-item
>
<v-list-item>{{ t("rom.cant-copy-link") }}:</v-list-item>
</v-row>
<v-row class="justify-center text-center pa-2 mb-3" no-gutters>
<v-list-item class="bg-terciary">{{ link }}</v-list-item>

View File

@@ -11,8 +11,10 @@ import type { Emitter } from "mitt";
import { computed, inject, ref } from "vue";
import { useRoute } from "vue-router";
import { useDisplay, useTheme } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const theme = useTheme();
const { lgAndUp, mdAndUp, smAndDown } = useDisplay();
const heartbeat = storeHeartbeat();
@@ -163,7 +165,7 @@ function closeDialog() {
<v-text-field
v-model="rom.name"
class="py-2"
label="Name"
:label="t('rom.name')"
variant="outlined"
required
hide-details
@@ -177,7 +179,7 @@ function closeDialog() {
v-model="rom.file_name"
class="py-2"
:rules="[(value: string) => !!value]"
label="Filename"
:label="rom.multi ? t('rom.foldername') : t('rom.filename')"
variant="outlined"
required
@keyup.enter="updateRom()"
@@ -200,7 +202,7 @@ function closeDialog() {
<v-textarea
v-model="rom.summary"
class="py-2"
label="Summary"
:label="t('rom.summary')"
variant="outlined"
required
hide-details
@@ -272,12 +274,14 @@ function closeDialog() {
variant="flat"
@click="unmatchRom"
>
Unmatch Rom
{{ t("rom.unmatch-rom") }}
</v-btn>
<v-btn-group divided density="compact">
<v-btn class="bg-terciary" @click="closeDialog"> Cancel </v-btn>
<v-btn class="bg-terciary" @click="closeDialog">
{{ t("common.cancel") }}
</v-btn>
<v-btn class="text-romm-green bg-terciary" @click="updateRom">
Save
{{ t("common.apply") }}
</v-btn>
</v-btn-group>
</v-row>

View File

@@ -11,6 +11,7 @@ import type { Emitter } from "mitt";
import { inject, onBeforeUnmount, ref } from "vue";
import { useRoute } from "vue-router";
import { useDisplay, useTheme } from "vuetify";
import { useI18n } from "vue-i18n";
type MatchedSource = {
url_cover: string | undefined;
@@ -19,6 +20,7 @@ type MatchedSource = {
};
// Props
const { t } = useI18n();
const { xs, lgAndUp } = useDisplay();
const show = ref(false);
const rom = ref<SimpleRom | null>(null);
@@ -229,7 +231,7 @@ onBeforeUnmount(() => {
:height="lgAndUp ? '90vh' : '775px'"
>
<template #header>
<span class="ml-4">Filter:</span>
<span class="ml-4">{{ t("common.filter") }}:</span>
<v-tooltip
location="top"
class="tooltip"
@@ -298,7 +300,7 @@ onBeforeUnmount(() => {
class="bg-terciary"
v-model="searchTerm"
:disabled="searching"
label="Search"
:label="t('common.search')"
hide-details
clearable
/>
@@ -306,7 +308,7 @@ onBeforeUnmount(() => {
<v-col cols="4" sm="3">
<v-select
:disabled="searching"
label="by"
:label="t('rom.by')"
class="bg-terciary"
:items="['ID', 'Name']"
v-model="searchBy"
@@ -371,7 +373,9 @@ onBeforeUnmount(() => {
<v-col cols="12">
<v-row no-gutters class="mt-4 justify-center text-center">
<v-col>
<span class="text-body-1">Select a cover image</span>
<span class="text-body-1">{{
t("rom.select-cover-image")
}}</span>
</v-col>
</v-row>
</v-col>
@@ -436,19 +440,21 @@ onBeforeUnmount(() => {
? "mdi-checkbox-outline"
: "mdi-checkbox-blank-outline"
}}</v-icon
>Rename file to match {{ selectedCover?.name }} title</v-chip
>{{
t("rom.rename-file-part1", { source: selectedCover?.name })
}}</v-chip
>
<v-list-item v-if="renameAsSource" class="mt-2">
<span>File will be renamed</span>
<span>{{ t("rom.rename-file-part2") }}</span>
<br />
<span>from</span
<span>{{ t("rom.rename-file-part3") }}</span
><span class="text-romm-accent-1 ml-1"
>{{ rom?.file_name_no_tags }}.{{
rom?.file_extension
}}</span
>
<br />
<span class="mx-1">to</span
<span class="mx-1">{{ t("rom.rename-file-part4") }}</span
><span class="text-romm-accent-2"
>{{ selectedMatchRom?.name }}.{{
rom?.file_extension
@@ -456,7 +462,7 @@ onBeforeUnmount(() => {
>
<br />
<span class="text-caption font-italic font-weight-bold"
>*File name tags won't be affected</span
>*{{ t("rom.rename-file-part5") }}</span
>
</v-list-item>
</v-col>
@@ -466,7 +472,7 @@ onBeforeUnmount(() => {
<v-row no-gutters class="my-4 justify-center">
<v-btn-group divided density="compact">
<v-btn class="bg-terciary" @click="backToMatched">
Cancel
{{ t("common.cancel") }}
</v-btn>
<v-btn
class="text-romm-green bg-terciary"
@@ -474,7 +480,7 @@ onBeforeUnmount(() => {
:variant="selectedCover == undefined ? 'plain' : 'flat'"
@click="confirm"
>
Confirm
{{ t("common.confirm") }}
</v-btn>
</v-btn-group>
</v-row>
@@ -485,10 +491,20 @@ onBeforeUnmount(() => {
<template #footer>
<v-row no-gutters class="text-center">
<v-col>
<v-chip variant="text">Results found:</v-chip>
<v-chip size="small" class="ml-1 text-romm-accent-1" label>{{
matchedRoms.length
}}</v-chip>
<v-chip label class="pr-0" size="small"
>{{ t("rom.results-found") }}:<v-chip
color="romm-accent-1"
class="ml-2 px-2"
label
>{{ !searching ? matchedRoms.length : ""
}}<v-progress-circular
v-if="searching"
:width="1"
:size="10"
color="romm-accent-1"
indeterminate
/></v-chip>
</v-chip>
</v-col>
</v-row>
</template>

View File

@@ -9,6 +9,7 @@ import type { Emitter } from "mitt";
import { inject, onBeforeUnmount, ref } from "vue";
import { useRouter } from "vue-router";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Define types
type Platform = {
@@ -21,6 +22,7 @@ type SelectItem = {
};
// Props
const { t } = useI18n();
const { lgAndUp } = useDisplay();
const show = ref(false);
const searching = ref(false);
@@ -116,7 +118,7 @@ onBeforeUnmount(() => {
@click:clear="searchRoms"
v-model="searchValue"
:disabled="searching"
label="Search"
:label="t('common.search')"
hide-details
class="bg-terciary"
/>
@@ -124,7 +126,7 @@ onBeforeUnmount(() => {
<v-col cols="5" lg="4">
<v-select
@click:clear="clearFilter"
label="Platform"
:label="t('common.platform')"
class="bg-terciary"
item-title="platform_name"
:disabled="platforms.length == 0 || searching"

View File

@@ -13,8 +13,10 @@ import { formatBytes } from "@/utils";
import type { Emitter } from "mitt";
import { inject, ref, watch } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const { xs, mdAndUp, smAndUp } = useDisplay();
const show = ref(false);
const filesToUpload = ref<File[]>([]);
@@ -184,7 +186,7 @@ watch(itemsPerPage, async () => {
<v-col cols="10" sm="8" lg="9">
<v-autocomplete
v-model="selectedPlatform"
label="Platform"
:label="t('common.platform')"
item-title="name"
:items="supportedPlatforms"
return-object
@@ -232,7 +234,7 @@ watch(itemsPerPage, async () => {
@click="triggerFileInput"
>
<v-icon :class="{ 'mr-2': !xs }"> mdi-plus </v-icon
><span v-if="!xs">Add</span>
><span v-if="!xs">{{ t("common.add") }}</span>
</v-btn>
<v-file-input
id="file-input"
@@ -315,7 +317,9 @@ watch(itemsPerPage, async () => {
<template #append>
<v-row class="justify-center mb-2" no-gutters>
<v-btn-group divided density="compact">
<v-btn class="bg-terciary" @click="closeDialog"> Cancel </v-btn>
<v-btn class="bg-terciary" @click="closeDialog">{{
t("common.cancel")
}}</v-btn>
<v-btn
class="bg-terciary text-romm-green"
:disabled="filesToUpload.length == 0 || selectedPlatform == null"
@@ -326,7 +330,7 @@ watch(itemsPerPage, async () => {
"
@click="uploadRoms"
>
Upload
{{ t("common.upload") }}
</v-btn>
</v-btn-group>
</v-row>

View File

@@ -8,8 +8,10 @@ import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import { inject } from "vue";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const navigationStore = storeNavigation();
const { smAndDown } = useDisplay();
const collectionsStore = storeCollections();
@@ -42,7 +44,7 @@ function clear() {
@click:clear="clear"
@update:model-value=""
single-line
label="Search collection"
:label="t('collection.search-collection')"
variant="solo-filled"
rounded="0"
></v-text-field>
@@ -63,7 +65,7 @@ function clear() {
size="large"
rounded="0"
block
>Add Collection</v-btn
>{{ t("collection.add-collection") }}</v-btn
>
</template>
</v-navigation-drawer>

View File

@@ -4,8 +4,10 @@ import storeNavigation from "@/stores/navigation";
import storePlatforms from "@/stores/platforms";
import { storeToRefs } from "pinia";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const navigationStore = storeNavigation();
const { smAndDown } = useDisplay();
const platformsStore = storePlatforms();
@@ -34,7 +36,7 @@ function clear() {
@click:clear="clear"
@update:model-value=""
single-line
label="Search platform"
:label="t('platform.search-platform')"
variant="solo-filled"
rounded="0"
></v-text-field>

View File

@@ -11,8 +11,10 @@ import { storeToRefs } from "pinia";
import { inject } from "vue";
import { useRouter } from "vue-router";
import { useDisplay } from "vuetify";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const navigationStore = storeNavigation();
const router = useRouter();
const auth = storeAuth();
@@ -70,36 +72,36 @@ async function logout() {
<v-list-item
@click="emitter?.emit('showEditUserDialog', auth.user as UserSchema)"
append-icon="mdi-account"
>Profile</v-list-item
>
<v-list-item :to="{ name: 'userInterface' }" append-icon="mdi-palette"
>User Interface</v-list-item
>{{ t("common.profile") }}</v-list-item
>
<v-list-item :to="{ name: 'userInterface' }" append-icon="mdi-palette">{{
t("common.user-interface")
}}</v-list-item>
<v-list-item
v-if="scopes.includes('platforms.write')"
append-icon="mdi-table-cog"
:to="{ name: 'libraryManagement' }"
>Library Management
>{{ t("common.library-management") }}
</v-list-item>
<v-list-item
v-if="scopes.includes('users.write')"
:to="{ name: 'administration' }"
append-icon="mdi-security"
>Administration</v-list-item
>{{ t("common.administration") }}</v-list-item
>
<template v-if="smAndDown">
<v-divider />
<v-list-item @click="logout" append-icon="mdi-location-exit"
>Logout</v-list-item
>
<v-list-item @click="logout" append-icon="mdi-location-exit">{{
t("common.logout")
}}</v-list-item>
</template>
</v-list>
<template v-if="!smAndDown" #append>
<v-list rounded="0" class="pa-0">
<v-divider />
<v-list-item @click="logout" append-icon="mdi-location-exit"
>Logout</v-list-item
>
<v-list-item @click="logout" append-icon="mdi-location-exit">{{
t("common.logout")
}}</v-list-item>
</v-list>
</template>
</v-navigation-drawer>

View File

@@ -8,7 +8,10 @@ import { inject, ref } from "vue";
import { useRouter } from "vue-router";
import { useDisplay } from "vuetify";
import PlatformIcon from "@/components/common/Platform/Icon.vue";
import { useI18n } from "vue-i18n";
// Props
const { t } = useI18n();
const router = useRouter();
const { lgAndUp } = useDisplay();
const platformsStore = storePlatforms();
@@ -20,6 +23,7 @@ emitter?.on("showDeletePlatformDialog", (platformToDelete) => {
show.value = true;
});
// Functions
async function deletePlatform() {
if (!platform.value) return;
@@ -65,23 +69,24 @@ function closeDialog() {
>
<template #content>
<v-row class="justify-center align-center pa-2" no-gutters>
<span class="mr-1">Removing platform</span>
<span class="mr-1">{{ t("platform.removing-platform-1") }}</span>
<platform-icon :slug="platform.slug" :name="platform.name" />
<span class="ml-1"
>{{ platform.name }} - [<span class="text-romm-accent-1">{{
platform.fs_slug
}}</span
>] from RomM.</span
>{{ t("platform.removing-platform-2") }}</span
>
<span class="ml-1">Do you confirm?</span>
</v-row>
</template>
<template #append>
<v-row class="justify-center pa-2" no-gutters>
<v-btn-group divided density="compact">
<v-btn class="bg-terciary" @click="closeDialog"> Cancel </v-btn>
<v-btn class="bg-terciary" @click="closeDialog">
{{ t("common.cancel") }}
</v-btn>
<v-btn class="bg-terciary text-romm-red" @click="deletePlatform">
Confirm
{{ t("common.confirm") }}
</v-btn>
</v-btn-group>
</v-row>

View File

@@ -0,0 +1,16 @@
{
"search-collection": "Sammlung durchsuchen",
"add-collection": "Sammlung hinzufügen",
"edit-collection": "Sammlung bearbeiten",
"name": "Name",
"description": "Beschreibung",
"owner": "Besitzer",
"public": "Öffentlich",
"public-desc": "Öffentlich (für alle sichtbar)",
"private": "Privat",
"private-desc": "Privat (nur für mich sichtbar)",
"danger-zone": "Gefahrenzone",
"delete-collection": "Sammlung löschen",
"removing-collection-1": "Löschen Sammlung",
"removing-collection-2": "aus RomM. Bist du sicher?"
}

View File

@@ -0,0 +1,27 @@
{
"platform": "Plattform",
"platforms": "Plattformen",
"firmware": "Firmware",
"games": "Spiele",
"collection": "Sammlung",
"collections": "Sammlungen",
"save": "Speichern",
"saves": "Speicherstände",
"state": "Spielstand",
"states": "Spielstände",
"screenshots": "Screenshots",
"search": "Suche",
"filter": "Filter",
"add": "Hinzufügen",
"upload": "Hochladen",
"cancel": "Abbrechen",
"confirm": "Bestätigen",
"apply": "Anwenden",
"update": "Update",
"create": "Erstellen",
"profile": "Profil",
"user-interface": "Benutzeroberfläche",
"library-management": "Bibliothek verwalten",
"administration": "Administration",
"logout": "Ausloggen"
}

View File

@@ -0,0 +1,3 @@
{
"recently-added": "Kürzlich hinzugefügt"
}

View File

@@ -0,0 +1,5 @@
{
"username": "Benutzername",
"password": "Passwort",
"login": "Einloggen"
}

View File

@@ -0,0 +1,31 @@
{
"search-platform": "Plattform suchen",
"upload-roms": "Roms hochladen",
"filesystem-folder-name": "Verzeichnisname im Dateisystem",
"category": "Kategorie",
"generation": "Generation",
"family": "Familie",
"settings": "Einstellungen",
"cover-style": "Cover-Stil",
"old-squared-cases": "Quadratische Boxen",
"danger-zone": "Gefahrenzone",
"delete-platform": "Plattform löschen",
"removing-platform-1": "Löschen Plattform",
"removing-platform-2": "] aus RomM. Bist du sicher?",
"show-firmwares": "Zeige Firmwares/BIOS",
"filter-gallery": "Gallerie filtern",
"show-unmatched": "Zeige unzugewiesene",
"show-favourites": "Zeige Favoriten",
"show-duplicates": "Zeige Duplikate",
"genre": "Genre",
"genres": "Genres",
"franchise": "Franchise",
"collection": "Sammlung",
"company": "Unternehmen",
"age-rating": "Altersfreigabe",
"status": "Status",
"reset-filters": "Filter zurücksetzen",
"active-multi-select": "Mehrfachauswahl aktivieren",
"change-view": "Ansicht anpassen",
"no-firmware-found": "Keine Firmware gefunden"
}

View File

@@ -0,0 +1,7 @@
{
"full-screen": "Vollbild",
"play": "Spielen",
"reset-session": "Session zurücksetzen",
"back-to-game-details": "Zurück zu den Spieldetails",
"back-to-gallery": "Zurück zur Plattformübersicht"
}

View File

@@ -0,0 +1,56 @@
{
"manual-match": "Manuell zuweisen",
"by": "nach",
"results-found": "Gefundene Ergebnisse",
"select-cover-image": "Wähle ein Coverbild",
"rename-file-part1": "Datei umbennen in gefundenen {source} Title",
"rename-file-part2": "Datei wird umbenannt",
"rename-file-part3": "von",
"rename-file-part4": "in",
"rename-file-part5": "Tags im Dateinamen werden nicht verändert",
"no-metadata-source": "Keine Quelle für Metadaten aktiv",
"edit-rom": "Bearbeiten",
"name": "Name",
"filename": "Dateiname",
"foldername": "Verzeichnisname",
"summary": "Zusammenfassung",
"unmatch-rom": "Zuweisung aufheben",
"add-to-fav": "Zu Favoriten hinzufügen",
"remove-from-fav": "Aus Favoriten entfernen",
"add-to-collection": "Zu Sammlung hinzufügen",
"adding-to-collection-part1": "Füge",
"adding-to-collection-part2": "Roms zu Sammlung hinzu",
"remove-from-collection-part1": "Entferne",
"remove-from-collection-part2": "Roms aus Sammlung",
"delete-rom": "Löschen",
"copy-link": "Download-Link kopieren",
"cant-copy-link": "Link kann nicht in Zwischenablage kopiert werden. Bitte manuell kopieren.",
"details": "Details",
"personal": "Persönlich",
"version": "Version",
"default": "Standard",
"set-as-default": "Rom als Standard setzen",
"file": "Datei",
"files": "Dateien",
"info": "Info",
"size": "Größe",
"tags": "Tags",
"genres": "Genres",
"franchises": "Franchises",
"collections": "Sammlungen",
"companies": "Unternehmen",
"age-rating": "Altersfreigabe",
"no-saves-found": "Keine Speicherstände gefunden",
"no-states-found": "Keine Spielstände gefunden",
"status": "Status",
"backlogged": "Vorgemerkt",
"now-playing": "Derzeit gespielt",
"hidden": "Versteckt",
"rating": "Bewertung",
"difficulty": "Schwierigkeitsgrad",
"completion": "Durchgespielt",
"my-notes": "Meine Notizen",
"public-notes": "Öffentliche Notizen",
"additional-content": "Zustätliche Inhalte",
"related-content": "Zugehörige Inhalte"
}

View File

@@ -0,0 +1,24 @@
{
"scan": "Scannen",
"abort": "Abbrechen",
"manage-library": "Bibliothek verwalten",
"metadata-sources": "Quellen für Metadaten",
"api-key-missing": "Fehlender oder ungültiger API-Key",
"scan-options": "Scan-Optionen",
"select-one-source": "Bitte wähle mindestens eine Metadatenquelle, wenn du die Bibliothek mit Cover-Artworks und Metadaten anreichern möchten",
"new-platforms": "Neue Platformen",
"new-platforms-desc": "Nur neue Plattformen scannen (am schnellsten)",
"quick-scan": "Schneller Scan",
"quick-scan-desc": "Nur neue Dateien scannen",
"unidentified-games": "Unidentifizierte Spiele",
"unidentified-games-desc": "Scanne Spiele ohne Metadaten",
"partial-metadata": "Unvollständige Metadaten",
"partial-metadata-desc": "Scanne Spiele mit unvollständigen Metadaten",
"complete-rescan": "Vollständiger Scan",
"complete-rescan-desc": "Kompletter Neu-Scan aller Plattformen und Dateien (am langsamsten)",
"no-new-roms": "Keine neuen oder geänderten Roms gefunden",
"platforms-scanned": "Plattformen: {n_platforms} gescannt",
"platforms-scanned-with-details": "Plattformen: {n_platforms} gescannt, darunter {n_added_platforms} neue und {n_identified_platforms} identifizierte",
"roms-scanned": "Roms: {n_roms} scanned",
"roms-scanned-with-details": "Roms: {n_platforms} gescannt, dabei {n_added_platforms} neue und {n_identified_platforms} identifizierte"
}

View File

@@ -0,0 +1,48 @@
{
"username": "Benutzername",
"password": "Passwort",
"role": "Rolle",
"language": "Sprache",
"theme": "Erscheinungsbild",
"theme-auto": "Auto",
"theme-dark": "Dunkel",
"theme-light": "Hell",
"interface": "Darstellungsoptionen",
"home": "Startseite",
"gallery": "Galerie",
"show-recently-added": "Zeige kürzlich hinzugefügte Roms",
"show-recently-added-desc": "Kürzlich hinzugefügte Roms werden auf der Startseite dargestellt",
"recently-added-as-grid": "Kürzlich hinzugefügte Roms als Raster darstellen",
"recently-added-as-grid-desc": "Kürzlich hinzugefügte Roms werden auf der Startseite als Raster gezeigt",
"show-platforms": "Zeige Plattformen",
"show-platforms-desc": "Plattformen werden auf der Startseite dargestellt",
"show-platforms-as-grid": "Plattformen als Raster darstellen",
"show-platforms-as-grid-desc": "Plattformen werden als Raster auf der Startseite gezeigt",
"show-collections": "Zeige Sammlungen",
"show-collections-desc": "Sammlungen werden auf der Startseite dargestellt",
"show-collections-as-grid": "Sammlungen als Raster darstellen",
"show-collections-as-grid-desc": "Sammlungen werden als Raster auf der Startseite gezeigt",
"group-roms": "Roms gruppieren",
"group-roms-desc": "Gruppiere verschiedene Versionen des gleichen Roms in der Galerie",
"show-siblings": "Zeige Anzahl der Versionen",
"show-siblings-desc": "Die Anzahl der verschiedenen Versionen einen Roms wird in der Galerie angezeigt, wenn die \"Roms gruppieren\" Option aktiviert ist",
"show-regions": "Zeige Regionen",
"show-regions-desc": "Zeige die Regionen des Roms als Flaggen in der Galerie",
"show-languages": "Zeige Sprachen",
"show-languages-desc": "Zeige die Sprachen des Roms in der Galerie",
"show-status": "Zeige Status",
"show-status-desc": "Ziegt den aktuellen Status eines Roms als Icons in der Galerie an (Vorgemerkt, derzeit gespielt, durchgespielt, etc)",
"platforms-bindings": "Platform-Zuweisung",
"platforms-versions": "Platform-Versionen",
"excluded": "Ausnahmen",
"excluded-single-rom-files": "Dateien in Plattform-Verzeichnissen",
"excluded-single-rom-extensions": "Dateiendungen in Plattform-Verzeichnissen",
"excluded-multi-rom-files": "Unterverzeichnisse in Plattform-Verzeichnissen",
"excluded-multi-rom-parts-files": "Dateien in Unterverzeichnisse in Plattform-Verzeichnissen",
"excluded-multi-rom-parts-extensions": "Dateiendungen in Unterverzeichnisse in Plattform-Verzeichnissen",
"folder-name": "Verzeichnisname",
"romm-platform": "RomM Plattform",
"platform-version": "Plattform-Version",
"main-platform": "Haupt-Plattform",
"join-discord": "Tritt unserem Discord bei"
}

View File

@@ -0,0 +1,16 @@
{
"search-collection": "Search collection",
"add-collection": "Add collection",
"edit-collection": "Edit collection",
"name": "Name",
"description": "Description",
"owner": "Owner",
"public": "Public",
"public-desc": "Pubilc (visible to everyone)",
"private": "Private",
"private-desc": "Private (only visible to me)",
"danger-zone": "Danger zone",
"delete-collection": "Delete collection",
"removing-collection-1": "Removing collection",
"removing-collection-2": "from RomM. Do you confirm?"
}

View File

@@ -0,0 +1,27 @@
{
"platform": "Platform",
"platforms": "Platforms",
"firmware": "Firmware",
"games": "Games",
"collection": "Collection",
"collections": "Collections",
"save": "Save",
"saves": "Saves",
"state": "State",
"states": "States",
"screenshots": "Screenshots",
"search": "Search",
"filter": "Filter",
"add": "Add",
"upload": "Upload",
"cancel": "Cancel",
"confirm": "Confirm",
"apply": "Apply",
"update": "Update",
"create": "Create",
"profile": "Profile",
"user-interface": "User interface",
"library-management": "Library management",
"administration": "Administration",
"logout": "Logout"
}

View File

@@ -0,0 +1,3 @@
{
"recently-added": "Recently added"
}

View File

@@ -0,0 +1,5 @@
{
"username": "Username",
"password": "Password",
"login": "Login"
}

View File

@@ -0,0 +1,31 @@
{
"search-platform": "Search platform",
"upload-roms": "Upload Roms",
"filesystem-folder-name": "Filesystem folder name",
"category": "Category",
"generation": "Generation",
"family": "Family",
"settings": "Settings",
"cover-style": "Cover style",
"old-squared-cases": "Old squared cases",
"danger-zone": "Danger zone",
"delete-platform": "Delete platform",
"removing-platform-1": "Removing platform",
"removing-platform-2": "] from RomM. Do you confirm?",
"show-firmwares": "Show firmwares/BIOS",
"filter-gallery": "Filter gallery",
"show-unmatched": "Show unmatched",
"show-favourites": "Show favourites",
"show-duplicates": "Show duplicates",
"genre": "Genre",
"genres": "Genres",
"franchise": "Franchise",
"collection": "Collection",
"company": "Company",
"age-rating": "Age rating",
"status": "Status",
"reset-filters": "Reset filters",
"active-multi-select": "Active multi-select",
"change-view": "Change view",
"no-firmware-found": "No firmware found"
}

View File

@@ -0,0 +1,7 @@
{
"full-screen": "Full Screen",
"play": "Play",
"reset-session": "Reset Session",
"back-to-game-details": "Back to game details",
"back-to-gallery": "Back to gallery"
}

View File

@@ -0,0 +1,56 @@
{
"manual-match": "Manual match",
"by": "by",
"results-found": "Results found",
"select-cover-image": "Select a cover image",
"rename-file-part1": "Rename file to match {source} title",
"rename-file-part2": "File will be renamed",
"rename-file-part3": "from",
"rename-file-part4": "to",
"rename-file-part5": "File name tags won't be affected",
"no-metadata-source": "No metadata source enabled",
"edit-rom": "Edit",
"name": "Name",
"filename": "File name",
"foldername": "Folder name",
"summary": "Summary",
"unmatch-rom": "Unmatch rom",
"add-to-fav": "Add to favourites",
"remove-from-fav": "Remove from favourites",
"add-to-collection": "Add to collection",
"adding-to-collection-part1": "Adding",
"adding-to-collection-part2": "roms to collection",
"remove-from-collection-part1": "Removing",
"remove-from-collection-part2": "roms from collection",
"delete-rom": "Delete",
"copy-link": "Copy download link",
"cant-copy-link": "Can't copy link to clipboard, copy it manually",
"details": "Details",
"personal": "Personal",
"version": "Version",
"default": "Default",
"set-as-default": "Set rom as default",
"file": "File",
"files": "Files",
"info": "Info",
"size": "Size",
"tags": "Tags",
"genres": "Genres",
"franchises": "Franchises",
"collections": "Collecciones",
"companies": "Companies",
"age-rating": "Age rating",
"no-saves-found": "No saves found",
"no-states-found": "No states found",
"status": "Status",
"backlogged": "Backlogged",
"now-playing": "Now playing",
"hidden": "Hidden",
"rating": "Rating",
"difficulty": "Difficulty",
"completion": "Completion",
"my-notes": "My notes",
"public-notes": "Public notes",
"additional-content": "Additional content",
"related-content": "Related content"
}

View File

@@ -0,0 +1,24 @@
{
"scan": "Scan",
"abort": "Abort",
"manage-library": "Manage library",
"metadata-sources": "Metadata sources",
"api-key-missing": "API key missing or invalid",
"scan-options": "Scan options",
"select-one-source": "Please select at least one metadata source if you want to enrich your library with artwork and metadata",
"new-platforms": "New platforms",
"new-platforms-desc": "Scan new platforms only (fastest)",
"quick-scan": "Quick scan",
"quick-scan-desc": "Scan new files only",
"unidentified-games": "Unidentified games",
"unidentified-games-desc": "Scan games with no metadata match",
"partial-metadata": "Partial metadata",
"partial-metadata-desc": "Scan games with partial metadata matches",
"complete-rescan": "Complete rescan",
"complete-rescan-desc": "Total rescan of all platforms and files (slowest)",
"no-new-roms": "No new/changed roms found",
"platforms-scanned": "Platforms: {n_platforms} scanned",
"platforms-scanned-with-details": "Platforms: {n_platforms} scanned, with {n_added_platforms} new and {n_identified_platforms} identified",
"roms-scanned": "Roms: {n_roms} scanned",
"roms-scanned-with-details": "Roms: {n_platforms} scanned, with {n_added_platforms} new and {n_identified_platforms} identified"
}

View File

@@ -0,0 +1,48 @@
{
"username": "Username",
"password": "Password",
"role": "Role",
"language": "Language",
"theme": "Theme",
"theme-auto": "Auto",
"theme-dark": "Dark",
"theme-light": "Light",
"interface": "Interface",
"home": "Home",
"gallery": "Gallery",
"show-recently-added": "Show recently added roms",
"show-recently-added-desc": "Show recently added roms section at the home page",
"recently-added-as-grid": "Recently added roms as grid",
"recently-added-as-grid-desc": "View recently added rom cards as a grid at the home page",
"show-platforms": "Show platforms",
"show-platforms-desc": "Show platforms section at the home page",
"show-platforms-as-grid": "Platforms as grid",
"show-platforms-as-grid-desc": "View platform cards as a grid at the home page",
"show-collections": "Show collections",
"show-collections-desc": "Show collections section at the home page",
"show-collections-as-grid": "Collections as grid",
"show-collections-as-grid-desc": "View collection cards as a grid at the home page",
"group-roms": "Group roms",
"group-roms-desc": "Group versions of the same rom together in the gallery",
"show-siblings": "Show siblings",
"show-siblings-desc": "Show siblings count in the gallery when \"Group roms\" option is enabled",
"show-regions": "Show regions",
"show-regions-desc": "Show region flags in the gallery",
"show-languages": "Show languages",
"show-languages-desc": "Show language flags in the gallery",
"show-status": "Show status",
"show-status-desc": "Show status icons in the gallery (backlogged, playing, completed, etc)",
"platforms-bindings": "Platforms bindings",
"platforms-versions": "Platforms versions",
"excluded": "Excluded",
"excluded-single-rom-files": "Single rom files",
"excluded-single-rom-extensions": "Single rom extensions",
"excluded-multi-rom-files": "Multi rom files",
"excluded-multi-rom-parts-files": "Multi rom parts files",
"excluded-multi-rom-parts-extensions": "Multi rom parts extensions",
"folder-name": "Folder name",
"romm-platform": "RomM platform",
"platform-version": "Platform version",
"main-platform": "Main platform",
"join-discord": "Join to our discord"
}

View File

@@ -0,0 +1,16 @@
{
"search-collection": "Search collection",
"add-collection": "Add collection",
"edit-collection": "Edit collection",
"name": "Name",
"description": "Description",
"owner": "Owner",
"public": "Public",
"public-desc": "Pubilc (visible to everyone)",
"private": "Private",
"private-desc": "Private (only visible to me)",
"danger-zone": "Danger zone",
"delete-collection": "Delete collection",
"removing-collection-1": "Removing collection",
"removing-collection-2": "from RomM. Do you confirm?"
}

View File

@@ -0,0 +1,27 @@
{
"platform": "Platform",
"platforms": "Platforms",
"firmware": "Firmware",
"games": "Games",
"collection": "Collection",
"collections": "Collections",
"save": "Save",
"saves": "Saves",
"state": "State",
"states": "States",
"screenshots": "Screenshots",
"search": "Search",
"filter": "Filter",
"add": "Add",
"upload": "Upload",
"cancel": "Cancel",
"confirm": "Confirm",
"apply": "Apply",
"update": "Update",
"create": "Create",
"profile": "Profile",
"user-interface": "User interface",
"library-management": "Library management",
"administration": "Administration",
"logout": "Logout"
}

View File

@@ -0,0 +1,3 @@
{
"recently-added": "Recently added"
}

View File

@@ -0,0 +1,5 @@
{
"username": "Username",
"password": "Password",
"login": "Login"
}

View File

@@ -0,0 +1,31 @@
{
"search-platform": "Search platform",
"upload-roms": "Upload Roms",
"filesystem-folder-name": "Filesystem folder name",
"category": "Category",
"generation": "Generation",
"family": "Family",
"settings": "Settings",
"cover-style": "Cover style",
"old-squared-cases": "Old squared cases",
"danger-zone": "Danger zone",
"delete-platform": "Delete platform",
"removing-platform-1": "Removing platform",
"removing-platform-2": "] from RomM. Do you confirm?",
"show-firmwares": "Show firmwares/BIOS",
"filter-gallery": "Filter gallery",
"show-unmatched": "Show unmatched",
"show-favourites": "Show favourites",
"show-duplicates": "Show duplicates",
"genre": "Genre",
"genres": "Genres",
"franchise": "Franchise",
"collection": "Collection",
"company": "Company",
"age-rating": "Age rating",
"status": "Status",
"reset-filters": "Reset filters",
"active-multi-select": "Active multi-select",
"change-view": "Change view",
"no-firmware-found": "No firmware found"
}

View File

@@ -0,0 +1,7 @@
{
"full-screen": "Full Screen",
"play": "Play",
"reset-session": "Reset Session",
"back-to-game-details": "Back to game details",
"back-to-gallery": "Back to gallery"
}

View File

@@ -0,0 +1,56 @@
{
"manual-match": "Manual match",
"by": "by",
"results-found": "Results found",
"select-cover-image": "Select a cover image",
"rename-file-part1": "Rename file to match {source} title",
"rename-file-part2": "File will be renamed",
"rename-file-part3": "from",
"rename-file-part4": "to",
"rename-file-part5": "File name tags won't be affected",
"no-metadata-source": "No metadata source enabled",
"edit-rom": "Edit",
"name": "Name",
"filename": "File name",
"foldername": "Folder name",
"summary": "Summary",
"unmatch-rom": "Unmatch rom",
"add-to-fav": "Add to favourites",
"remove-from-fav": "Remove from favourites",
"add-to-collection": "Add to collection",
"adding-to-collection-part1": "Adding",
"adding-to-collection-part2": "roms to collection",
"remove-from-collection-part1": "Removing",
"remove-from-collection-part2": "roms from collection",
"delete-rom": "Delete",
"copy-link": "Copy download link",
"cant-copy-link": "Can't copy link to clipboard, copy it manually",
"details": "Details",
"personal": "Personal",
"version": "Version",
"default": "Default",
"set-as-default": "Set rom as default",
"file": "File",
"files": "Files",
"info": "Info",
"size": "Size",
"tags": "Tags",
"genres": "Genres",
"franchises": "Franchises",
"collections": "Collecciones",
"companies": "Companies",
"age-rating": "Age rating",
"no-saves-found": "No saves found",
"no-states-found": "No states found",
"status": "Status",
"backlogged": "Backlogged",
"now-playing": "Now playing",
"hidden": "Hidden",
"rating": "Rating",
"difficulty": "Difficulty",
"completion": "Completion",
"my-notes": "My notes",
"public-notes": "Public notes",
"additional-content": "Additional content",
"related-content": "Related content"
}

View File

@@ -0,0 +1,24 @@
{
"scan": "Scan",
"abort": "Abort",
"manage-library": "Manage library",
"metadata-sources": "Metadata sources",
"api-key-missing": "API key missing or invalid",
"scan-options": "Scan options",
"select-one-source": "Please select at least one metadata source if you want to enrich your library with artwork and metadata",
"new-platforms": "New platforms",
"new-platforms-desc": "Scan new platforms only (fastest)",
"quick-scan": "Quick scan",
"quick-scan-desc": "Scan new files only",
"unidentified-games": "Unidentified games",
"unidentified-games-desc": "Scan games with no metadata match",
"partial-metadata": "Partial metadata",
"partial-metadata-desc": "Scan games with partial metadata matches",
"complete-rescan": "Complete rescan",
"complete-rescan-desc": "Total rescan of all platforms and files (slowest)",
"no-new-roms": "No new/changed roms found",
"platforms-scanned": "Platforms: {n_platforms} scanned",
"platforms-scanned-with-details": "Platforms: {n_platforms} scanned, with {n_added_platforms} new and {n_identified_platforms} identified",
"roms-scanned": "Roms: {n_roms} scanned",
"roms-scanned-with-details": "Roms: {n_platforms} scanned, with {n_added_platforms} new and {n_identified_platforms} identified"
}

View File

@@ -0,0 +1,48 @@
{
"username": "Username",
"password": "Password",
"role": "Role",
"language": "Language",
"theme": "Theme",
"theme-auto": "Auto",
"theme-dark": "Dark",
"theme-light": "Light",
"interface": "Interface",
"home": "Home",
"gallery": "Gallery",
"show-recently-added": "Show recently added roms",
"show-recently-added-desc": "Show recently added roms section at the home page",
"recently-added-as-grid": "Recently added roms as grid",
"recently-added-as-grid-desc": "View recently added rom cards as a grid at the home page",
"show-platforms": "Show platforms",
"show-platforms-desc": "Show platforms section at the home page",
"show-platforms-as-grid": "Platforms as grid",
"show-platforms-as-grid-desc": "View platform cards as a grid at the home page",
"show-collections": "Show collections",
"show-collections-desc": "Show collections section at the home page",
"show-collections-as-grid": "Collections as grid",
"show-collections-as-grid-desc": "View collection cards as a grid at the home page",
"group-roms": "Group roms",
"group-roms-desc": "Group versions of the same rom together in the gallery",
"show-siblings": "Show siblings",
"show-siblings-desc": "Show siblings count in the gallery when \"Group roms\" option is enabled",
"show-regions": "Show regions",
"show-regions-desc": "Show region flags in the gallery",
"show-languages": "Show languages",
"show-languages-desc": "Show language flags in the gallery",
"show-status": "Show status",
"show-status-desc": "Show status icons in the gallery (backlogged, playing, completed, etc)",
"platforms-bindings": "Platforms bindings",
"platforms-versions": "Platforms versions",
"excluded": "Excluded",
"excluded-single-rom-files": "Single rom files",
"excluded-single-rom-extensions": "Single rom extensions",
"excluded-multi-rom-files": "Multi rom files",
"excluded-multi-rom-parts-files": "Multi rom parts files",
"excluded-multi-rom-parts-extensions": "Multi rom parts extensions",
"folder-name": "Folder name",
"romm-platform": "RomM platform",
"platform-version": "Platform version",
"main-platform": "Main platform",
"join-discord": "Join to our discord"
}

View File

@@ -0,0 +1,16 @@
{
"search-collection": "Buscar colección",
"add-collection": "Añadir colección",
"edit-collection": "Editar colección",
"name": "Nombre",
"description": "Descripción",
"owner": "Propietario",
"public": "Pública",
"public-desc": "Pública (visible para todos)",
"private": "Privada",
"private-desc": "Privada (visible solo para mi)",
"danger-zone": "Zona de peligro",
"delete-collection": "Eliminar colección",
"removing-collection-1": "Eliminando colección",
"removing-collection-2": "de RomM. ¿Confirmar?"
}

View File

@@ -0,0 +1,27 @@
{
"platform": "Plataforma",
"platforms": "Plataformas",
"firmware": "Firmware",
"games": "Juegos",
"collection": "Colección",
"collections": "Colecciones",
"save": "Guardado",
"saves": "Guardados",
"state": "Estado",
"states": "Estados",
"screenshots": "Capturas de pantalla",
"search": "Buscar",
"filter": "Filtrar",
"add": "Añadir",
"upload": "Subir",
"cancel": "Cancelar",
"confirm": "Confirmar",
"apply": "Aplicar",
"update": "Actualizar",
"create": "Crear",
"profile": "Perfil",
"user-interface": "Interfaz de usuario",
"library-management": "Gestionar biblioteca",
"administration": "Administración",
"logout": "Cerrar sesión"
}

View File

@@ -0,0 +1,3 @@
{
"recently-added": "Añadido recientemente"
}

View File

@@ -0,0 +1,5 @@
{
"username": "Usuario",
"password": "Contraseña",
"login": "Iniciar sesión"
}

View File

@@ -0,0 +1,30 @@
{
"search-platform": "Buscar plataforma",
"upload-roms": "Subir Roms",
"filesystem-folder-name": "Nombre de carpeta",
"category": "Categoría",
"generation": "Generación",
"family": "Familia",
"settings": "Configuración",
"cover-style": "Estilo de carátula",
"old-squared-cases": "Caja cuadrada clásica",
"danger-zone": "Zona de peligro",
"delete-platform": "Eliminar platforma",
"removing-platform-1": "Eliminando",
"removing-platform-2": "] de RomM. ¿Confirmar?",
"show-firmwares": "Mostrar firmwares/BIOS",
"filter-gallery": "Filtrar galería",
"show-unmatched": "Mostrar no identificados",
"show-favourites": "Mostrar favoritos",
"show-duplicates": "Mostrar duplicados",
"genre": "Género",
"franchise": "Franquicia",
"collection": "Colección",
"company": "Compañía",
"age-rating": "Clasificación por edad",
"status": "Estado",
"reset-filters": "Restablecer filtros",
"active-multi-select": "Activar selección múltiple",
"change-view": "Cambiar vista",
"no-firmware-found": "No se encontraron firmwares"
}

View File

@@ -0,0 +1,7 @@
{
"full-screen": "Pantalla completa",
"play": "Jugar",
"reset-session": "Restablecer sesión",
"back-to-game-details": "Volver a detalles",
"back-to-gallery": "Volver a galería"
}

View File

@@ -0,0 +1,56 @@
{
"manual-match": "Identificación manual",
"by": "por",
"results-found": "Resultados encontrados",
"select-cover-image": "Selecciona una carátula",
"rename-file-part1": "Renombrar archivo para coincidir con el título de {source}",
"rename-file-part2": "El fichero será renombrado",
"rename-file-part3": "de",
"rename-file-part4": "a",
"rename-file-part5": "Las etiquetas del nombre no se verán afectadas",
"no-metadata-source": "No hay fuente de metadatos habilitada",
"edit-rom": "Editar",
"name": "Nombre",
"filename": "Nombre del archivo",
"foldername": "Nombre de la carpeta",
"summary": "Resumen",
"unmatch-rom": "Desvincular rom",
"add-to-fav": "Añadir a favoritos",
"remove-from-fav": "Eliminar de favoritos",
"add-to-collection": "Añadir a la colección",
"adding-to-collection-part1": "Añadiendo",
"adding-to-collection-part2": "roms a la colección",
"remove-from-collection-part1": "Quitando",
"remove-from-collection-part2": "roms de la colección",
"delete-rom": "Eliminar",
"copy-link": "Copiar link de descarga",
"cant-copy-link": "No se pudo copiar el link al portapapeles, copialo manualmente",
"details": "Detalles",
"personal": "Personal",
"version": "Versión",
"default": "Por defecto",
"set-as-default": "Establecer rom por defecto",
"file": "Archivo",
"files": "Archivos",
"info": "Info",
"size": "Tamaño",
"tags": "Etiquetas",
"genres": "Géneros",
"franchises": "Franquicias",
"collections": "Colecciones",
"companies": "Compañías",
"age-rating": "Clasificación por edad",
"no-saves-found": "No se encontraron partidas guardadas",
"no-states-found": "No se encontraron estados",
"status": "Estado",
"backlogged": "Pendiente",
"now-playing": "Jugando",
"hidden": "Oculto",
"rating": "Valoración",
"difficulty": "Dificultad",
"completion": "Completado",
"my-notes": "Mis notas",
"public-notes": "Notas públicas",
"additional-content": "Contenido adicional",
"related-content": "Contenido relacionado"
}

View File

@@ -0,0 +1,24 @@
{
"scan": "Escanear",
"abort": "Cancelar",
"manage-library": "Gestionar biblioteca",
"metadata-sources": "Fuentes de metadatos",
"api-key-missing": "API key inválida o no configurada",
"scan-options": "Tipo de escaneo",
"select-one-source": "Por favor, elige al menos una fuente de metadatos si quieres enriquecer tu biblioteca con carátulas y metadatos",
"new-platforms": "Plataformas nuevas",
"new-platforms-desc": "Escanea únicamente plataformas añadidas recientemente (más rápido)",
"quick-scan": "Escaneo rápido",
"quick-scan-desc": "Escanea tu biblioteca en busca nuevos ficheros",
"unidentified-games": "Juegos sin identificar",
"unidentified-games-desc": "Escanea únicamente juegos sin identificar por ninguna de las fuentes de metadatos",
"partial-metadata": "Metadatos parciales",
"partial-metadata-desc": "Escanea únicamente juegos identificados con metadatos a medias",
"complete-rescan": "Escaneo completo",
"complete-rescan-desc": "Escaneo completo de todos los ficheros y plataformas (más lento)",
"no-new-roms": "No se han encontrado ficheros nuevos o modificados",
"platforms-scanned": "Plataformas: {n_platforms} escaneadas",
"platforms-scanned-with-details": "Plataformas: {n_platforms} escaneadas, {n_added_platforms} nuevas y {n_identified_platforms} identificadas",
"roms-scanned": "Roms: {n_roms} escaneadas",
"roms-scanned-with-details": "Roms: {n_platforms} escaneadas, {n_added_platforms} nuevas y {n_identified_platforms} identificadas"
}

View File

@@ -0,0 +1,48 @@
{
"username": "Nombre de usuario",
"password": "Contraseña",
"role": "Rol",
"language": "Idioma",
"theme": "Tema",
"theme-auto": "Auto",
"theme-dark": "Oscuro",
"theme-light": "Claro",
"interface": "Interfaz",
"home": "Página principal",
"gallery": "Galería",
"show-recently-added": "Mostrar roms añadidos recientemente",
"show-recently-added-desc": "Mostrar la sección de roms añadidos recientemente en la página principal",
"recently-added-as-grid": "Mostrar roms añadidos recientemente como cuadrícula",
"recently-added-as-grid-desc": "Mostrar la sección de roms añadidos recientemente como cuadrícula en la página principal",
"show-platforms": "Mostrar plataformas",
"show-platforms-desc": "Mostrar la sección de plataformas en la página principal",
"show-platforms-as-grid": "Mostrar plataformas como cuadrícula",
"show-platforms-as-grid-desc": "Mostrar la sección de plataformas como cuadrícula en la página principal",
"show-collections": "Mostrar colecciones",
"show-collections-desc": "Mostrar la sección de colecciones en la página principal",
"show-collections-as-grid": "Mostrar colecciones como cuadrícula",
"show-collections-as-grid-desc": "Mostrar la sección de colecciones como cuadrícula en la página principal",
"group-roms": "Agrupar roms",
"group-roms-desc": "Agrupar roms con la misma versión en una única entrada en la galería",
"show-siblings": "Mostrar siblings",
"show-siblings-desc": "Mostrar el número de \"hermanos\" en la galería cuando la opción \"Agrupar roms\" está habilitada",
"show-regions": "Mostrar regiones",
"show-regions-desc": "Mostrar banderas de region en la galería",
"show-languages": "Mostrar idiomas",
"show-languages-desc": "Mostrar banderas de idioma en la galería",
"show-status": "Mostrar estado",
"show-status-desc": "Mostrar icono de estado en la galería (backlogged, playing, completed, etc)",
"platforms-bindings": "Asociaciones de plataformas",
"platforms-versions": "Versiones de plataformas",
"excluded": "Exclusiones",
"excluded-single-rom-files": "Roms de fichero único",
"excluded-single-rom-extensions": "Extensiones de roms de fichero único",
"excluded-multi-rom-files": "Roms multi fichero",
"excluded-multi-rom-parts-files": "Partes de roms multi fichero",
"excluded-multi-rom-parts-extensions": "Extensiones de partes de roms multi fichero",
"folder-name": "Nombre de la carpeta",
"romm-platform": "Plataforma en RomM",
"platform-version": "Version de la plataforma",
"main-platform": "Plataforma principal",
"join-discord": "Únete a nuestro discord"
}

View File

@@ -0,0 +1,16 @@
{
"search-collection": "Recherche dans la collection",
"add-collection": "Ajouter une collection",
"edit-collection": "Modifier la collection",
"name": "Nom",
"description": "Description",
"owner": "Propriétaire",
"public": "Public",
"public-desc": "Public (visible par tous)",
"private": "Privé",
"private-desc": "Privé (visible uniquement par moi)",
"danger-zone": "Zone de danger",
"delete-collection": "Supprimer la collection",
"removing-collection-1": "Confirmer le retrait de",
"removing-collection-2": "la collection de RomM?"
}

Some files were not shown because too many files have changed in this diff Show More