Skip to content

Add Multilingual Support (EN, IT, DE, ES, FR) #156

New issue

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

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

Already on GitHub? Sign in to your account

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions src/lib/apis/tmdb/tmdb-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { settings } from '../../stores/settings.store';
import type { TitleType } from '../../types';
import type { Api } from '../api.interface';
import { user } from '../../stores/user.store';
import { localSettings } from '../../stores/localstorage.store';

const CACHE_ONE_DAY = 'max-age=86400';
const CACHE_FOUR_DAYS = 'max-age=345600';
Expand Down Expand Up @@ -113,8 +114,9 @@ export class TmdbApi implements Api<paths> {
movie_id: tmdbId
},
query: {
language: get(localSettings)?.language,
append_to_response: 'videos,credits,external_ids,images',
...({ include_image_language: get(settings)?.language + ',en,null' } as any)
...({ include_image_language: get(localSettings)?.language + ',en,null' } as any)
}
}
})
Expand All @@ -126,7 +128,7 @@ export class TmdbApi implements Api<paths> {
?.GET('/3/movie/popular', {
params: {
query: {
language: get(settings)?.language,
language: get(localSettings)?.language,
region: get(settings)?.discover.region
}
}
Expand All @@ -143,6 +145,7 @@ export class TmdbApi implements Api<paths> {
external_id: tvdbId
},
query: {

external_source: 'tvdb_id'
}
},
Expand All @@ -167,8 +170,9 @@ export class TmdbApi implements Api<paths> {
series_id: tmdbId
},
query: {
language: get(localSettings)?.language,
append_to_response: 'videos,aggregate_credits,external_ids,images',
...({ include_image_language: get(settings)?.language + ',en,null' } as any)
...({ include_image_language: get(localSettings)?.language + ',en,null' } as any)
}
},
headers: {
Expand Down Expand Up @@ -213,7 +217,7 @@ export class TmdbApi implements Api<paths> {
.GET('/3/tv/popular', {
params: {
query: {
language: get(settings)?.language
language: get(localSettings)?.language
}
}
})
Expand Down Expand Up @@ -255,6 +259,7 @@ export class TmdbApi implements Api<paths> {
episode_number: episode
},
query: {
language: get(localSettings)?.language, // Spécifiez la langue ici
append_to_response: 'credits,external_ids,images'
}
}
Expand All @@ -280,6 +285,7 @@ export class TmdbApi implements Api<paths> {
person_id: person_id
},
query: {
language: get(localSettings)?.language,
append_to_response: 'images,movie_credits,tv_credits,external_ids'
}
}
Expand Down Expand Up @@ -338,6 +344,7 @@ export class TmdbApi implements Api<paths> {
account_object_id: userId
},
query: {
language: get(localSettings)?.language,
page: i + 1
}
}
Expand Down Expand Up @@ -413,6 +420,7 @@ export class TmdbApi implements Api<paths> {
account_object_id: userId
},
query: {
language: get(localSettings)?.language,
page: i + 1
}
}
Expand Down Expand Up @@ -536,7 +544,7 @@ export const getTmdbMovie = async (tmdbId: number) =>
},
query: {
append_to_response: 'videos,credits,external_ids,images',
...({ include_image_language: get(settings)?.language + ',en,null' } as any)
...({ include_image_language: get(localSettings)?.language + ',en,null' } as any)
}
}
}).then((res) => res.data as TmdbMovieFull2 | undefined);
Expand All @@ -548,7 +556,8 @@ export const getTmdbSeriesFromTvdbId = async (tvdbId: string) =>
external_id: tvdbId
},
query: {
external_source: 'tvdb_id'
external_source: 'tvdb_id',
language: get(localSettings)?.language
}
},
headers: {
Expand All @@ -571,7 +580,7 @@ export const getTmdbSeries = async (tmdbId: number): Promise<TmdbSeriesFull2 | u
},
query: {
append_to_response: 'videos,aggregate_credits,external_ids,images',
...({ include_image_language: get(settings)?.language + ',en,null' } as any)
language: get(localSettings)?.language
}
},
headers: {
Expand Down Expand Up @@ -643,7 +652,7 @@ export const getTmdbMovieBackdrop = async (tmdbId: number) =>
.then(
(r) =>
(
r?.backdrops?.find((b) => b.iso_639_1 === get(settings)?.language) ||
r?.backdrops?.find((b) => b.iso_639_1 === get(localSettings)?.language) ||
r?.backdrops?.find((b) => b.iso_639_1 === 'en') ||
r?.backdrops?.find((b) => b.iso_639_1) ||
r?.backdrops?.[0]
Expand Down Expand Up @@ -735,7 +744,7 @@ export const getTmdbItemBackdrop = (item: {
images: { backdrops: { file_path: string; iso_639_1: string }[] };
}) =>
(
item?.images?.backdrops?.find((b) => b.iso_639_1 === get(settings)?.language) ||
item?.images?.backdrops?.find((b) => b.iso_639_1 === get(localSettings)?.language) ||
item?.images?.backdrops?.find((b) => b.iso_639_1 === 'en') ||
item?.images?.backdrops?.find((b) => b.iso_639_1) ||
item?.images?.backdrops?.[0]
Expand Down
13 changes: 7 additions & 6 deletions src/lib/components/ContextMenu/LibraryItemContextItems.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import type { TitleType } from '../../types';
import ContextMenuDivider from './ContextMenuDivider.svelte';
import ContextMenuItem from './ContextMenuItem.svelte';
import { _ } from 'svelte-i18n';

export let jellyfinItem: JellyfinItem | undefined = undefined;
export let sonarrSeries: SonarrSeries | undefined = undefined;
Expand Down Expand Up @@ -42,30 +43,30 @@
</script>

<ContextMenuItem on:click={handleSetWatched} disabled={!jellyfinItem?.Id || watched}>
Mark as watched
{_('contextMenu.markAsWatched')}
</ContextMenuItem>
<ContextMenuItem on:click={handleSetUnwatched} disabled={!jellyfinItem?.Id || !watched}>
Mark as unwatched
{_('contextMenu.markAsUnwatched')}
</ContextMenuItem>
<ContextMenuDivider />
<ContextMenuItem disabled={!jellyfinItem?.Id} on:click={handleOpenInJellyfin}>
Open in Jellyfin
{_('contextMenu.openInJellyfin')}
</ContextMenuItem>
{#if type === 'movie'}
<ContextMenuItem
disabled={!radarrMovie}
on:click={() => window.open($settings.radarr.baseUrl + '/movie/' + radarrMovie?.tmdbId)}
>
Open in Radarr
{_('contextMenu.openInRadarr')}
</ContextMenuItem>
{:else}
<ContextMenuItem
disabled={!sonarrSeries}
on:click={() => window.open($settings.sonarr.baseUrl + '/series/' + sonarrSeries?.titleSlug)}
>
Open in Sonarr
{_('contextMenu.openInSonarr')}
</ContextMenuItem>
{/if}
<ContextMenuItem on:click={() => window.open(`https://www.themoviedb.org/${type}/${tmdbId}`)}>
Open in TMDB
{_('contextMenu.openInTMDB')}
</ContextMenuItem>
5 changes: 3 additions & 2 deletions src/lib/components/Dialog/ConfirmDialog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import Button from '../Button.svelte';
import { modalStack } from '../Modal/modal.store';
import Dialog from './Dialog.svelte';
import { _ } from 'svelte-i18n';

type ActionFn = (() => Promise<any>) | (() => any);

Expand Down Expand Up @@ -38,10 +39,10 @@
</div>
<Container class="flex flex-col space-y-4">
<Button type="secondary" disabled={fetching} on:clickOrSelect={() => handleAction(confirm)}>
Confirm
{$_('confirmDialogs.confirm')}
</Button>
<Button type="secondary" disabled={fetching} on:clickOrSelect={() => handleAction(cancel)}
>Cancel</Button
>{$_('confirmDialogs.cancel')}</Button
>
</Container>
</Dialog>
23 changes: 12 additions & 11 deletions src/lib/components/Dialog/CreateOrEditProfileModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import { navigate } from '../StackRouter/StackRouter';
import Toggle from '../Toggle.svelte';
import { get } from 'svelte/store';
import { _ } from 'svelte-i18n';

enum Tabs {
EditProfile,
Expand Down Expand Up @@ -166,11 +167,11 @@
<Dialog class="grid" size={'dynamic'}>
<Tab {...tab} tab={Tabs.EditProfile} class="space-y-4 max-w-lg">
<h1 class="header2">
{createNew ? 'Create Account' : 'Edit Profile'}
</h1>
<TextField bind:value={name}>name</TextField>
{createNew ? $_('settings.profile.createProfile') : $_('settings.profile.editProfile')}
</h1>
<TextField bind:value={name}>{$_('settings.profile.name')}</TextField>
<SelectField value={profilePictureTitle} on:clickOrSelect={() => tab.set(Tabs.ProfilePictures)}>
Profile Picture
{$_('settings.profile.profilePicture')}
</SelectField>
{#if !createNew}
<Container direction="horizontal" class="flex space-x-4 items-end">
Expand All @@ -179,7 +180,7 @@
bind:value={oldPassword}
type={oldPasswordVisible ? 'text' : 'password'}
>
Old Password
{$_('settings.profile.oldPassword')}
</TextField>
<IconToggle
on:clickOrSelect={() => (oldPasswordVisible = !oldPasswordVisible)}
Expand All @@ -193,7 +194,7 @@
bind:value={newPassword}
type={newPasswordVisible ? 'text' : 'password'}
>
New Password
{$_('settings.profile.newPassword')}
</TextField>
<IconToggle
on:clickOrSelect={() => (newPasswordVisible = !newPasswordVisible)}
Expand All @@ -202,7 +203,7 @@
</Container>
{#if isAdmin || admin}
<div class="flex justify-between">
<label>Admin</label>
<label>{$_('settings.profile.admin')}</label>
<Toggle bind:checked={isAdmin}>Admin</Toggle>
</div>
{/if}
Expand All @@ -211,7 +212,7 @@
{/if}
<Container direction="horizontal" class="flex space-x-4 pt-4 *:flex-1">
{#if !createNew}
<Button type="primary-dark" disabled={!stale} action={save}>Save</Button>
<Button type="primary-dark" disabled={!stale} action={save}>{$_('settings.profile.save')}</Button>
<Button
type="primary-dark"
icon={Trash}
Expand All @@ -220,10 +221,10 @@
header: 'Delete Account',
body: 'Are you sure you want to delete your account?',
confirm: handleDeleteAccount
})}>Delete Account</Button
})}>{$_('settings.profile.deleteAccount')}</Button
>
{:else}
<Button type="primary-dark" disabled={!complete} action={create}>Create</Button>
<Button type="primary-dark" disabled={!complete} action={create}>{$_('settings.accounts.create')}</Button>
{/if}
</Container>
</Tab>
Expand All @@ -236,7 +237,7 @@
detail.stopPropagation();
}}
>
<h1 class="header2 mb-6">Select Profile Picture</h1>
<h1 class="header2 mb-6">{$_('settings.profile.profilePicture')}</h1>
<Container direction="grid" gridCols={3} class="grid grid-cols-3 gap-4 w-max">
<ProfileIcon
url={profilePictures.ana}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/components/Ghosts/ButtonGhost.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<script lang="ts">
import Container from '../../../Container.svelte';
import { _ } from 'svelte-i18n';
</script>

<Container
class="px-6 py-2 rounded-lg font-medium tracking-wide flex items-center bg-stone-600/50 animate-pulse"
>
<div class="opacity-0">
<slot>Loading...</slot>
<slot>{$_('navbar.loading')}</slot>
</div>
</Container>
7 changes: 4 additions & 3 deletions src/lib/components/Integrations/JellyfinIntegration.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import SelectField from '../SelectField.svelte';
import { jellyfinApi, type JellyfinUser } from '../../apis/jellyfin/jellyfin-api';
import { derived, get } from 'svelte/store';
import { _ } from 'svelte-i18n';

const dispatch = createEventDispatcher<{
'click-user': {
Expand Down Expand Up @@ -108,14 +109,14 @@
isValid={jellyfinUsers?.then((u) => !!u?.length)}
on:change={handleChange}
>
Base Url
{$_('settings.integrations.baseUrl')}
</TextField>
<TextField
bind:value={apiKey}
isValid={jellyfinUsers?.then((u) => !!u?.length)}
on:change={handleChange}
>
API Key
{$_('settings.integrations.apiKey')}
</TextField>
</div>

Expand All @@ -127,7 +128,7 @@
dispatch('click-user', { user: jellyfinUser, users, setJellyfinUser })}
class="mb-4"
>
User
{$_('settings.integrations.user')}
</SelectField>
{/if}
{/await}
Expand Down
5 changes: 3 additions & 2 deletions src/lib/components/Integrations/RadarrIntegration.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { radarrApi } from '../../apis/radarr/radarr-api';
import { user } from '../../stores/user.store';
import { derived, get } from 'svelte/store';
import { _ } from 'svelte-i18n';

let baseUrl = get(user)?.settings.radarr.baseUrl || '';
let apiKey = get(user)?.settings.radarr.apiKey || '';
Expand Down Expand Up @@ -85,9 +86,9 @@

<div class="space-y-4 mb-4">
<TextField bind:value={baseUrl} isValid={healthCheck} on:change={handleChange}>
Base Url
{$_('settings.integrations.baseUrl')}
</TextField>
<TextField bind:value={apiKey} isValid={healthCheck} on:change={handleChange}>API Key</TextField>
<TextField bind:value={apiKey} isValid={healthCheck} on:change={handleChange}>{$_('settings.integrations.apiKey')}</TextField>
</div>

{#if error}
Expand Down
5 changes: 3 additions & 2 deletions src/lib/components/Integrations/SonarrIntegration.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { createEventDispatcher } from 'svelte';
import { user } from '../../stores/user.store';
import { derived, get } from 'svelte/store';
import { _ } from 'svelte-i18n';

let baseUrl = get(user)?.settings.sonarr.baseUrl || '';
let apiKey = get(user)?.settings.sonarr.apiKey || '';
Expand Down Expand Up @@ -85,9 +86,9 @@

<div class="space-y-4 mb-4">
<TextField bind:value={baseUrl} isValid={healthCheck} on:change={handleChange}>
Base Url
{$_('settings.integrations.baseUrl')}
</TextField>
<TextField bind:value={apiKey} isValid={healthCheck} on:change={handleChange}>API Key</TextField>
<TextField bind:value={apiKey} isValid={healthCheck} on:change={handleChange}>{$_('settings.integrations.apiKey')}</TextField>
</div>

{#if error}
Expand Down
Loading