first commit

This commit is contained in:
2025-09-14 23:05:30 -04:00
commit 2866d74b32
122 changed files with 95474 additions and 0 deletions
+233
View File
@@ -0,0 +1,233 @@
import { Injectable, computed, signal } from '@angular/core';
export type LangCode = 'en' | 'fr';
// Simple key-value dictionaries. Extend as needed.
const EN: Record<string, string> = {
'app.name': 'NewTube',
// Header
'search.placeholder': 'Search...',
'label.provider': 'Provider',
'label.region': 'Region',
// Regions
'region.US': 'United States',
'region.FR': 'France',
'region.BR': 'Brazil',
'region.DE': 'Germany',
'region.GB': 'United Kingdom',
'region.CA': 'Canada',
'btn.edit': 'Edit',
'btn.login': 'Login',
'btn.register': 'Register',
'menu.account': 'Account',
'menu.preferences': 'Preferences',
'menu.appearance': 'Appearance',
'menu.logout': 'Logout',
'theme.black': 'Black',
'theme.dark': 'Dark',
'theme.blue': 'Blue',
'theme.light': 'Light',
// Preferences
'preferences.title': 'Preferences',
'preferences.general': 'General',
'preferences.general_hint': 'Customize your experience',
'preferences.peertube_hint': 'Manage your PeerTube instances',
'preferences.add_instance_placeholder': 'e.g. example.org',
'preferences.no_instances': 'No instances yet.',
'preferences.active': 'Active',
'preferences.set_active': 'Set Active',
'label.language': 'Language',
'label.defaultProvider': 'Default Provider',
'label.theme': 'Theme',
'label.quality': 'Default Quality',
'btn.save': 'Save',
'btn.add': 'Add',
'btn.remove': 'Remove',
// Sidebar
'nav.home': 'Home',
'nav.shorts': 'Shorts',
'nav.subscriptions': 'Subscriptions',
'nav.you': 'You',
'nav.history': 'History',
'nav.playlists': 'Playlists',
'nav.liked': 'Liked videos',
'nav.explore': 'Explore',
'explore.music': 'Music',
'explore.gaming': 'Gaming',
'explore.news': 'News',
'explore.sports': 'Sports',
'explore.learning': 'Learning',
// Home
'home.trending': 'Trending',
'loading.more': 'Loading more...',
'search.hint': 'Type something in the search bar to get started.',
'search.noResults': 'No results found.',
// Themes + Provider/Theme pages
'themes.viewAll': 'View All',
'themes.live': 'Live Streams',
'themes.recorded': 'Recorded Streams',
'themes.videos': 'Videos',
'themes.categories': 'Categories',
'themes.backToThemes': 'Back to themes',
'themes.changeProvider': 'Change provider',
// Common
'common.error': 'An error occurred.',
'empty.noItems': 'No items.',
'common.more': 'More',
'common.less': 'Less',
// Filters
'filter.sort': 'Sort by',
'filter.sort.recent': 'Most recent',
'filter.sort.viewed': 'Most viewed',
'filter.sort.longest': 'Longest',
'filter.duration': 'Duration',
'filter.duration.any': 'Any',
'filter.duration.short': 'Short (<10m)',
'filter.duration.medium': 'Medium (10–30m)',
'filter.duration.long': 'Long (30m+)',
'filter.language': 'Language',
'filter.date': 'Date',
// Optional per-theme labels
'theme.finance-crypto': 'Finance & Crypto',
'theme.gaming': 'Gaming',
'theme.news': 'News',
'theme.sports': 'Sports',
};
const FR: Record<string, string> = {
'app.name': 'NewTube',
// Header
'search.placeholder': 'Rechercher...',
'label.provider': 'Fournisseur',
'label.region': 'Région',
// Régions
'region.US': 'États-Unis',
'region.FR': 'France',
'region.BR': 'Brésil',
'region.DE': 'Allemagne',
'region.GB': 'Royaume-Uni',
'region.CA': 'Canada',
'btn.edit': 'Modifier',
'btn.login': 'Connexion',
'btn.register': 'Inscription',
'menu.account': 'Compte',
'menu.preferences': 'Préférences',
'menu.appearance': 'Apparence',
'menu.logout': 'Déconnexion',
'theme.black': 'Noir',
'theme.dark': 'Sombre',
'theme.blue': 'Bleu',
'theme.light': 'Clair',
// Preferences
'preferences.title': 'Préférences',
'preferences.general': 'Préférences générales',
'preferences.general_hint': 'Personnalisez votre expérience',
'preferences.peertube_hint': 'Gérez vos instances PeerTube',
'preferences.add_instance_placeholder': 'ex: example.org',
'preferences.no_instances': 'Aucune instance pour le moment.',
'preferences.active': 'Actif',
'preferences.set_active': 'Définir actif',
'label.language': 'Langue',
'label.defaultProvider': 'Fournisseur par défaut',
'label.theme': 'Thème',
'label.quality': 'Qualité par défaut',
'btn.save': 'Enregistrer',
'btn.add': 'Ajouter',
'btn.remove': 'Supprimer',
// Sidebar
'nav.home': 'Accueil',
'nav.shorts': 'Shorts',
'nav.subscriptions': 'Abonnements',
'nav.you': 'Vous',
'nav.history': 'Historique',
'nav.playlists': 'Listes de lecture',
'nav.liked': 'Vidéos que vous aimez',
'nav.explore': 'Explorer',
'explore.music': 'Musique',
'explore.gaming': 'Jeux vidéo',
'explore.news': 'Actualités',
'explore.sports': 'Sports',
'explore.learning': 'Apprentissage',
// Home
'home.trending': 'Tendances',
'loading.more': 'Chargement...',
'search.hint': 'Tapez quelque chose dans la barre de recherche pour commencer.',
'search.noResults': 'Aucun résultat.',
// Themes + Provider/Theme pages
'themes.viewAll': 'Voir tout',
'themes.live': 'En direct',
'themes.recorded': 'Rediffusions',
'themes.videos': 'Vidéos',
'themes.categories': 'Catégories',
'themes.backToThemes': 'Retour aux thèmes',
'themes.changeProvider': 'Changer de provider',
// Common
'common.error': 'Une erreur est survenue.',
'empty.noItems': 'Aucun élément.',
'common.more': 'Plus',
'common.less': 'Moins',
// Filters
'filter.sort': 'Tri',
'filter.sort.recent': 'Plus récents',
'filter.sort.viewed': 'Plus vus',
'filter.sort.longest': 'Plus longs',
'filter.duration': 'Durée',
'filter.duration.any': 'Toute',
'filter.duration.short': 'Courte (<10m)',
'filter.duration.medium': 'Moyenne (10–30m)',
'filter.duration.long': 'Longue (30m+)',
'filter.language': 'Langue',
'filter.date': 'Date',
// Optional per-theme labels
'theme.finance-crypto': 'Finance & Crypto',
'theme.gaming': 'Jeux vidéo',
'theme.news': 'Actualités',
'theme.sports': 'Sports',
};
@Injectable({ providedIn: 'root' })
export class I18nService {
private readonly LS_KEY = 'newtube.lang';
private readonly dictionaries: Record<string, Record<string, string>> = { en: EN, fr: FR };
private readonly defaultLang: LangCode = 'en';
// initialize from localStorage, fallback 'en'
lang = signal<LangCode>((() => {
try {
const ls = (localStorage.getItem(this.LS_KEY) || '').toLowerCase();
return (ls === 'fr' ? 'fr' : 'en') as LangCode;
} catch {
return 'en';
}
})());
readonly currentDict = computed(() => this.dictionaries[this.lang()] || this.dictionaries[this.defaultLang]);
constructor() {
try {
if (typeof document !== 'undefined') {
document.documentElement.setAttribute('lang', this.lang());
}
} catch {}
}
setLanguage(l: string | null | undefined) {
const v = (String(l || '').toLowerCase() === 'fr') ? 'fr' : 'en';
this.lang.set(v as LangCode);
try {
localStorage.setItem(this.LS_KEY, v);
if (typeof document !== 'undefined') {
document.documentElement.setAttribute('lang', v);
}
} catch {}
}
t(key: string, params?: Record<string, any>): string {
const dict = this.currentDict();
const base = dict[key] ?? this.dictionaries[this.defaultLang][key] ?? key;
if (params) {
return Object.keys(params).reduce((acc, k) => acc.replace(new RegExp('{' + k + '}', 'g'), String(params[k])), base);
}
return base;
}
}