Merge branch 'main' into feat/mfa
This commit is contained in:
@@ -2,10 +2,20 @@ import React, { createContext, useContext, useMemo, ReactNode } from 'react'
|
||||
import { useSettingsStore } from '../store/settingsStore'
|
||||
import de from './translations/de'
|
||||
import en from './translations/en'
|
||||
import es from './translations/es'
|
||||
|
||||
type TranslationStrings = Record<string, string>
|
||||
|
||||
const translations: Record<string, TranslationStrings> = { de, en }
|
||||
const translations: Record<string, TranslationStrings> = { de, en, es }
|
||||
const LOCALES: Record<string, string> = { de: 'de-DE', en: 'en-US', es: 'es-ES' }
|
||||
|
||||
export function getLocaleForLanguage(language: string): string {
|
||||
return LOCALES[language] || LOCALES.en
|
||||
}
|
||||
|
||||
export function getIntlLanguage(language: string): string {
|
||||
return language === 'de' || language === 'es' ? language : 'en'
|
||||
}
|
||||
|
||||
interface TranslationContextValue {
|
||||
t: (key: string, params?: Record<string, string | number>) => string
|
||||
@@ -13,18 +23,18 @@ interface TranslationContextValue {
|
||||
locale: string
|
||||
}
|
||||
|
||||
const TranslationContext = createContext<TranslationContextValue>({ t: (k: string) => k, language: 'de', locale: 'de-DE' })
|
||||
const TranslationContext = createContext<TranslationContextValue>({ t: (k: string) => k, language: 'en', locale: 'en-US' })
|
||||
|
||||
interface TranslationProviderProps {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export function TranslationProvider({ children }: TranslationProviderProps) {
|
||||
const language = useSettingsStore((s) => s.settings.language) || 'de'
|
||||
const language = useSettingsStore((s) => s.settings.language) || 'en'
|
||||
|
||||
const value = useMemo((): TranslationContextValue => {
|
||||
const strings = translations[language] || translations.de
|
||||
const fallback = translations.de
|
||||
const strings = translations[language] || translations.en
|
||||
const fallback = translations.en
|
||||
|
||||
function t(key: string, params?: Record<string, string | number>): string {
|
||||
let val: string = strings[key] ?? fallback[key] ?? key
|
||||
@@ -36,7 +46,7 @@ export function TranslationProvider({ children }: TranslationProviderProps) {
|
||||
return val
|
||||
}
|
||||
|
||||
return { t, language, locale: language === 'en' ? 'en-US' : 'de-DE' }
|
||||
return { t, language, locale: getLocaleForLanguage(language) }
|
||||
}, [language])
|
||||
|
||||
return <TranslationContext.Provider value={value}>{children}</TranslationContext.Provider>
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { TranslationProvider, useTranslation } from './TranslationContext'
|
||||
export { TranslationProvider, useTranslation, getLocaleForLanguage, getIntlLanguage } from './TranslationContext'
|
||||
|
||||
@@ -207,7 +207,7 @@ const de: Record<string, string> = {
|
||||
'login.signingIn': 'Anmelden…',
|
||||
'login.signIn': 'Anmelden',
|
||||
'login.createAdmin': 'Admin-Konto erstellen',
|
||||
'login.createAdminHint': 'Erstelle das erste Admin-Konto für NOMAD.',
|
||||
'login.createAdminHint': 'Erstelle das erste Admin-Konto für TREK.',
|
||||
'login.createAccount': 'Konto erstellen',
|
||||
'login.createAccountHint': 'Neues Konto registrieren.',
|
||||
'login.creating': 'Erstelle…',
|
||||
@@ -222,6 +222,7 @@ const de: Record<string, string> = {
|
||||
'login.oidc.invalidState': 'Ungültige Sitzung. Bitte erneut versuchen.',
|
||||
'login.demoFailed': 'Demo-Login fehlgeschlagen',
|
||||
'login.oidcSignIn': 'Anmelden mit {name}',
|
||||
'login.oidcOnly': 'Passwort-Authentifizierung ist deaktiviert. Bitte melde dich über deinen SSO-Anbieter an.',
|
||||
'login.demoHint': 'Demo ausprobieren — ohne Registrierung',
|
||||
'login.mfaTitle': 'Zwei-Faktor-Authentifizierung',
|
||||
'login.mfaSubtitle': 'Gib den 6-stelligen Code aus deiner Authenticator-App ein.',
|
||||
@@ -308,6 +309,8 @@ const de: Record<string, string> = {
|
||||
'admin.oidcIssuer': 'Issuer URL',
|
||||
'admin.oidcIssuerHint': 'Die OpenID Connect Issuer URL des Anbieters. z.B. https://accounts.google.com',
|
||||
'admin.oidcSaved': 'OIDC-Konfiguration gespeichert',
|
||||
'admin.oidcOnlyMode': 'Passwort-Authentifizierung deaktivieren',
|
||||
'admin.oidcOnlyModeHint': 'Wenn aktiviert, ist nur SSO-Login erlaubt. Passwort-Login und Registrierung werden blockiert.',
|
||||
|
||||
// File Types
|
||||
'admin.fileTypes': 'Erlaubte Dateitypen',
|
||||
@@ -318,7 +321,7 @@ const de: Record<string, string> = {
|
||||
// Addons
|
||||
'admin.tabs.addons': 'Addons',
|
||||
'admin.addons.title': 'Addons',
|
||||
'admin.addons.subtitle': 'Aktiviere oder deaktiviere Funktionen, um NOMAD nach deinen Wünschen anzupassen.',
|
||||
'admin.addons.subtitle': 'Aktiviere oder deaktiviere Funktionen, um TREK nach deinen Wünschen anzupassen.',
|
||||
'admin.addons.subtitleBefore': 'Aktiviere oder deaktiviere Funktionen, um ',
|
||||
'admin.addons.subtitleAfter': ' nach deinen Wünschen anzupassen.',
|
||||
'admin.addons.enabled': 'Aktiviert',
|
||||
@@ -333,7 +336,7 @@ const de: Record<string, string> = {
|
||||
// Weather info
|
||||
'admin.weather.title': 'Wetterdaten',
|
||||
'admin.weather.badge': 'Seit 24. März 2026',
|
||||
'admin.weather.description': 'NOMAD nutzt Open-Meteo als Wetterdatenquelle. Open-Meteo ist ein kostenloser, quelloffener Wetterdienst — es wird kein API-Schlüssel benötigt.',
|
||||
'admin.weather.description': 'TREK nutzt Open-Meteo als Wetterdatenquelle. Open-Meteo ist ein kostenloser, quelloffener Wetterdienst — es wird kein API-Schlüssel benötigt.',
|
||||
'admin.weather.forecast': '16-Tage-Vorhersage',
|
||||
'admin.weather.forecastDesc': 'Statt bisher 5 Tage (OpenWeatherMap)',
|
||||
'admin.weather.climate': 'Historische Klimadaten',
|
||||
@@ -356,11 +359,11 @@ const de: Record<string, string> = {
|
||||
'admin.github.by': 'von',
|
||||
|
||||
'admin.update.available': 'Update verfügbar',
|
||||
'admin.update.text': 'NOMAD {version} ist verfügbar. Du verwendest {current}.',
|
||||
'admin.update.text': 'TREK {version} ist verfügbar. Du verwendest {current}.',
|
||||
'admin.update.button': 'Auf GitHub ansehen',
|
||||
'admin.update.install': 'Update installieren',
|
||||
'admin.update.confirmTitle': 'Update installieren?',
|
||||
'admin.update.confirmText': 'NOMAD wird von {current} auf {version} aktualisiert. Der Server startet danach automatisch neu.',
|
||||
'admin.update.confirmText': 'TREK wird von {current} auf {version} aktualisiert. Der Server startet danach automatisch neu.',
|
||||
'admin.update.dataInfo': 'Alle Daten (Reisen, Benutzer, API-Schlüssel, Uploads, Vacay, Atlas, Budgets) bleiben erhalten.',
|
||||
'admin.update.warning': 'Die App ist während des Neustarts kurz nicht erreichbar.',
|
||||
'admin.update.confirm': 'Jetzt aktualisieren',
|
||||
@@ -370,7 +373,7 @@ const de: Record<string, string> = {
|
||||
'admin.update.backupHint': 'Wir empfehlen, vor dem Update ein Backup zu erstellen und herunterzuladen.',
|
||||
'admin.update.backupLink': 'Zum Backup',
|
||||
'admin.update.howTo': 'Update-Anleitung',
|
||||
'admin.update.dockerText': 'Deine NOMAD-Instanz läuft in Docker. Um auf {version} zu aktualisieren, führe folgende Befehle auf deinem Server aus:',
|
||||
'admin.update.dockerText': 'Deine TREK-Instanz läuft in Docker. Um auf {version} zu aktualisieren, führe folgende Befehle auf deinem Server aus:',
|
||||
'admin.update.reloadHint': 'Bitte lade die Seite in wenigen Sekunden neu.',
|
||||
|
||||
// Vacay addon
|
||||
@@ -416,9 +419,9 @@ const de: Record<string, string> = {
|
||||
'vacay.carryOver': 'Urlaubsmitnahme',
|
||||
'vacay.carryOverHint': 'Resturlaub automatisch ins Folgejahr übertragen',
|
||||
'vacay.sharing': 'Teilen',
|
||||
'vacay.sharingHint': 'Teile deinen Urlaubsplan mit anderen NOMAD-Benutzern',
|
||||
'vacay.sharingHint': 'Teile deinen Urlaubsplan mit anderen TREK-Benutzern',
|
||||
'vacay.owner': 'Besitzer',
|
||||
'vacay.shareEmailPlaceholder': 'E-Mail des NOMAD-Benutzers',
|
||||
'vacay.shareEmailPlaceholder': 'E-Mail des TREK-Benutzers',
|
||||
'vacay.shareSuccess': 'Plan erfolgreich geteilt',
|
||||
'vacay.shareError': 'Plan konnte nicht geteilt werden',
|
||||
'vacay.dissolve': 'Fusion auflösen',
|
||||
@@ -430,7 +433,7 @@ const de: Record<string, string> = {
|
||||
'vacay.noData': 'Keine Daten',
|
||||
'vacay.changeColor': 'Farbe ändern',
|
||||
'vacay.inviteUser': 'Benutzer einladen',
|
||||
'vacay.inviteHint': 'Lade einen anderen NOMAD-Benutzer ein, um einen gemeinsamen Urlaubskalender zu teilen.',
|
||||
'vacay.inviteHint': 'Lade einen anderen TREK-Benutzer ein, um einen gemeinsamen Urlaubskalender zu teilen.',
|
||||
'vacay.selectUser': 'Benutzer wählen',
|
||||
'vacay.sendInvite': 'Einladung senden',
|
||||
'vacay.inviteSent': 'Einladung gesendet',
|
||||
@@ -609,6 +612,23 @@ const de: Record<string, string> = {
|
||||
'reservations.timeAlt': 'Uhrzeit (alternativ, z.B. 19:30)',
|
||||
'reservations.notes': 'Notizen',
|
||||
'reservations.notesPlaceholder': 'Zusätzliche Notizen...',
|
||||
'reservations.meta.airline': 'Airline',
|
||||
'reservations.meta.flightNumber': 'Flugnr.',
|
||||
'reservations.meta.from': 'Von',
|
||||
'reservations.meta.to': 'Nach',
|
||||
'reservations.meta.trainNumber': 'Zugnr.',
|
||||
'reservations.meta.platform': 'Gleis',
|
||||
'reservations.meta.seat': 'Sitzplatz',
|
||||
'reservations.meta.checkIn': 'Check-in',
|
||||
'reservations.meta.checkOut': 'Check-out',
|
||||
'reservations.meta.linkAccommodation': 'Unterkunft',
|
||||
'reservations.meta.pickAccommodation': 'Mit Unterkunft verknüpfen',
|
||||
'reservations.meta.noAccommodation': 'Keine',
|
||||
'reservations.meta.hotelPlace': 'Hotel',
|
||||
'reservations.meta.pickHotel': 'Hotel auswählen',
|
||||
'reservations.meta.fromDay': 'Von',
|
||||
'reservations.meta.toDay': 'Bis',
|
||||
'reservations.meta.selectDay': 'Tag wählen',
|
||||
'reservations.type.flight': 'Flug',
|
||||
'reservations.type.hotel': 'Hotel',
|
||||
'reservations.type.restaurant': 'Restaurant',
|
||||
@@ -702,6 +722,28 @@ const de: Record<string, string> = {
|
||||
'files.sourceBooking': 'Buchung',
|
||||
'files.attach': 'Anhängen',
|
||||
'files.pasteHint': 'Du kannst auch Bilder aus der Zwischenablage einfügen (Strg+V)',
|
||||
'files.trash': 'Papierkorb',
|
||||
'files.trashEmpty': 'Papierkorb ist leer',
|
||||
'files.emptyTrash': 'Papierkorb leeren',
|
||||
'files.restore': 'Wiederherstellen',
|
||||
'files.star': 'Markieren',
|
||||
'files.unstar': 'Markierung entfernen',
|
||||
'files.assign': 'Zuweisen',
|
||||
'files.assignTitle': 'Datei zuweisen',
|
||||
'files.assignPlace': 'Ort',
|
||||
'files.assignBooking': 'Buchung',
|
||||
'files.unassigned': 'Nicht zugewiesen',
|
||||
'files.unlink': 'Verknüpfung entfernen',
|
||||
'files.toast.trashed': 'In den Papierkorb verschoben',
|
||||
'files.toast.restored': 'Datei wiederhergestellt',
|
||||
'files.toast.trashEmptied': 'Papierkorb geleert',
|
||||
'files.toast.assigned': 'Datei zugewiesen',
|
||||
'files.toast.assignError': 'Zuweisung fehlgeschlagen',
|
||||
'files.toast.restoreError': 'Wiederherstellung fehlgeschlagen',
|
||||
'files.confirm.permanentDelete': 'Diese Datei endgültig löschen? Das kann nicht rückgängig gemacht werden.',
|
||||
'files.confirm.emptyTrash': 'Alle Dateien im Papierkorb endgültig löschen? Das kann nicht rückgängig gemacht werden.',
|
||||
'files.noteLabel': 'Notiz',
|
||||
'files.notePlaceholder': 'Notiz hinzufügen...',
|
||||
|
||||
// Packing
|
||||
'packing.title': 'Packliste',
|
||||
@@ -991,7 +1033,6 @@ const de: Record<string, string> = {
|
||||
'collab.chat.justNow': 'gerade eben',
|
||||
'collab.chat.minutesAgo': 'vor {n} Min.',
|
||||
'collab.chat.hoursAgo': 'vor {n} Std.',
|
||||
'collab.chat.yesterday': 'gestern',
|
||||
'collab.notes.title': 'Notizen',
|
||||
'collab.notes.new': 'Neue Notiz',
|
||||
'collab.notes.empty': 'Noch keine Notizen',
|
||||
|
||||
@@ -207,7 +207,7 @@ const en: Record<string, string> = {
|
||||
'login.signingIn': 'Signing in…',
|
||||
'login.signIn': 'Sign In',
|
||||
'login.createAdmin': 'Create Admin Account',
|
||||
'login.createAdminHint': 'Set up the first admin account for NOMAD.',
|
||||
'login.createAdminHint': 'Set up the first admin account for TREK.',
|
||||
'login.createAccount': 'Create Account',
|
||||
'login.createAccountHint': 'Register a new account.',
|
||||
'login.creating': 'Creating…',
|
||||
@@ -222,6 +222,7 @@ const en: Record<string, string> = {
|
||||
'login.oidc.invalidState': 'Invalid session. Please try again.',
|
||||
'login.demoFailed': 'Demo login failed',
|
||||
'login.oidcSignIn': 'Sign in with {name}',
|
||||
'login.oidcOnly': 'Password authentication is disabled. Please sign in using your SSO provider.',
|
||||
'login.demoHint': 'Try the demo — no registration needed',
|
||||
'login.mfaTitle': 'Two-factor authentication',
|
||||
'login.mfaSubtitle': 'Enter the 6-digit code from your authenticator app.',
|
||||
@@ -308,6 +309,8 @@ const en: Record<string, string> = {
|
||||
'admin.oidcIssuer': 'Issuer URL',
|
||||
'admin.oidcIssuerHint': 'The OpenID Connect Issuer URL of the provider. e.g. https://accounts.google.com',
|
||||
'admin.oidcSaved': 'OIDC configuration saved',
|
||||
'admin.oidcOnlyMode': 'Disable password authentication',
|
||||
'admin.oidcOnlyModeHint': 'When enabled, only SSO login is permitted. Password-based login and registration are blocked.',
|
||||
|
||||
// File Types
|
||||
'admin.fileTypes': 'Allowed File Types',
|
||||
@@ -318,7 +321,7 @@ const en: Record<string, string> = {
|
||||
// Addons
|
||||
'admin.tabs.addons': 'Addons',
|
||||
'admin.addons.title': 'Addons',
|
||||
'admin.addons.subtitle': 'Enable or disable features to customize your NOMAD experience.',
|
||||
'admin.addons.subtitle': 'Enable or disable features to customize your TREK experience.',
|
||||
'admin.addons.subtitleBefore': 'Enable or disable features to customize your ',
|
||||
'admin.addons.subtitleAfter': ' experience.',
|
||||
'admin.addons.enabled': 'Enabled',
|
||||
@@ -333,7 +336,7 @@ const en: Record<string, string> = {
|
||||
// Weather info
|
||||
'admin.weather.title': 'Weather Data',
|
||||
'admin.weather.badge': 'Since March 24, 2026',
|
||||
'admin.weather.description': 'NOMAD uses Open-Meteo as its weather data source. Open-Meteo is a free, open-source weather service — no API key required.',
|
||||
'admin.weather.description': 'TREK uses Open-Meteo as its weather data source. Open-Meteo is a free, open-source weather service — no API key required.',
|
||||
'admin.weather.forecast': '16-day forecast',
|
||||
'admin.weather.forecastDesc': 'Previously 5 days (OpenWeatherMap)',
|
||||
'admin.weather.climate': 'Historical climate data',
|
||||
@@ -356,11 +359,11 @@ const en: Record<string, string> = {
|
||||
'admin.github.by': 'by',
|
||||
|
||||
'admin.update.available': 'Update available',
|
||||
'admin.update.text': 'NOMAD {version} is available. You are running {current}.',
|
||||
'admin.update.text': 'TREK {version} is available. You are running {current}.',
|
||||
'admin.update.button': 'View on GitHub',
|
||||
'admin.update.install': 'Install Update',
|
||||
'admin.update.confirmTitle': 'Install Update?',
|
||||
'admin.update.confirmText': 'NOMAD will be updated from {current} to {version}. The server will restart automatically afterwards.',
|
||||
'admin.update.confirmText': 'TREK will be updated from {current} to {version}. The server will restart automatically afterwards.',
|
||||
'admin.update.dataInfo': 'All your data (trips, users, API keys, uploads, Vacay, Atlas, budgets) will be preserved.',
|
||||
'admin.update.warning': 'The app will be briefly unavailable during the restart.',
|
||||
'admin.update.confirm': 'Update Now',
|
||||
@@ -370,7 +373,7 @@ const en: Record<string, string> = {
|
||||
'admin.update.backupHint': 'We recommend creating a backup before updating.',
|
||||
'admin.update.backupLink': 'Go to Backup',
|
||||
'admin.update.howTo': 'How to Update',
|
||||
'admin.update.dockerText': 'Your NOMAD instance runs in Docker. To update to {version}, run the following commands on your server:',
|
||||
'admin.update.dockerText': 'Your TREK instance runs in Docker. To update to {version}, run the following commands on your server:',
|
||||
'admin.update.reloadHint': 'Please reload the page in a few seconds.',
|
||||
|
||||
// Vacay addon
|
||||
@@ -416,9 +419,9 @@ const en: Record<string, string> = {
|
||||
'vacay.carryOver': 'Carry Over',
|
||||
'vacay.carryOverHint': 'Automatically carry remaining vacation days into the next year',
|
||||
'vacay.sharing': 'Sharing',
|
||||
'vacay.sharingHint': 'Share your vacation plan with other NOMAD users',
|
||||
'vacay.sharingHint': 'Share your vacation plan with other TREK users',
|
||||
'vacay.owner': 'Owner',
|
||||
'vacay.shareEmailPlaceholder': 'Email of NOMAD user',
|
||||
'vacay.shareEmailPlaceholder': 'Email of TREK user',
|
||||
'vacay.shareSuccess': 'Plan shared successfully',
|
||||
'vacay.shareError': 'Could not share plan',
|
||||
'vacay.dissolve': 'Dissolve Fusion',
|
||||
@@ -430,7 +433,7 @@ const en: Record<string, string> = {
|
||||
'vacay.noData': 'No data',
|
||||
'vacay.changeColor': 'Change color',
|
||||
'vacay.inviteUser': 'Invite User',
|
||||
'vacay.inviteHint': 'Invite another NOMAD user to share a combined vacation calendar.',
|
||||
'vacay.inviteHint': 'Invite another TREK user to share a combined vacation calendar.',
|
||||
'vacay.selectUser': 'Select user',
|
||||
'vacay.sendInvite': 'Send Invite',
|
||||
'vacay.inviteSent': 'Invite sent',
|
||||
@@ -609,6 +612,23 @@ const en: Record<string, string> = {
|
||||
'reservations.timeAlt': 'Time (alternative, e.g. 19:30)',
|
||||
'reservations.notes': 'Notes',
|
||||
'reservations.notesPlaceholder': 'Additional notes...',
|
||||
'reservations.meta.airline': 'Airline',
|
||||
'reservations.meta.flightNumber': 'Flight No.',
|
||||
'reservations.meta.from': 'From',
|
||||
'reservations.meta.to': 'To',
|
||||
'reservations.meta.trainNumber': 'Train No.',
|
||||
'reservations.meta.platform': 'Platform',
|
||||
'reservations.meta.seat': 'Seat',
|
||||
'reservations.meta.checkIn': 'Check-in',
|
||||
'reservations.meta.checkOut': 'Check-out',
|
||||
'reservations.meta.linkAccommodation': 'Accommodation',
|
||||
'reservations.meta.pickAccommodation': 'Link to accommodation',
|
||||
'reservations.meta.noAccommodation': 'None',
|
||||
'reservations.meta.hotelPlace': 'Hotel',
|
||||
'reservations.meta.pickHotel': 'Select hotel',
|
||||
'reservations.meta.fromDay': 'From',
|
||||
'reservations.meta.toDay': 'To',
|
||||
'reservations.meta.selectDay': 'Select day',
|
||||
'reservations.type.flight': 'Flight',
|
||||
'reservations.type.hotel': 'Hotel',
|
||||
'reservations.type.restaurant': 'Restaurant',
|
||||
@@ -702,6 +722,28 @@ const en: Record<string, string> = {
|
||||
'files.sourceBooking': 'Booking',
|
||||
'files.attach': 'Attach',
|
||||
'files.pasteHint': 'You can also paste images from clipboard (Ctrl+V)',
|
||||
'files.trash': 'Trash',
|
||||
'files.trashEmpty': 'Trash is empty',
|
||||
'files.emptyTrash': 'Empty Trash',
|
||||
'files.restore': 'Restore',
|
||||
'files.star': 'Star',
|
||||
'files.unstar': 'Unstar',
|
||||
'files.assign': 'Assign',
|
||||
'files.assignTitle': 'Assign File',
|
||||
'files.assignPlace': 'Place',
|
||||
'files.assignBooking': 'Booking',
|
||||
'files.unassigned': 'Unassigned',
|
||||
'files.unlink': 'Remove link',
|
||||
'files.toast.trashed': 'Moved to trash',
|
||||
'files.toast.restored': 'File restored',
|
||||
'files.toast.trashEmptied': 'Trash emptied',
|
||||
'files.toast.assigned': 'File assigned',
|
||||
'files.toast.assignError': 'Assignment failed',
|
||||
'files.toast.restoreError': 'Restore failed',
|
||||
'files.confirm.permanentDelete': 'Permanently delete this file? This cannot be undone.',
|
||||
'files.confirm.emptyTrash': 'Permanently delete all trashed files? This cannot be undone.',
|
||||
'files.noteLabel': 'Note',
|
||||
'files.notePlaceholder': 'Add a note...',
|
||||
|
||||
// Packing
|
||||
'packing.title': 'Packing List',
|
||||
@@ -991,7 +1033,6 @@ const en: Record<string, string> = {
|
||||
'collab.chat.justNow': 'just now',
|
||||
'collab.chat.minutesAgo': '{n}m ago',
|
||||
'collab.chat.hoursAgo': '{n}h ago',
|
||||
'collab.chat.yesterday': 'yesterday',
|
||||
'collab.notes.title': 'Notes',
|
||||
'collab.notes.new': 'New Note',
|
||||
'collab.notes.empty': 'No notes yet',
|
||||
|
||||
1061
client/src/i18n/translations/es.js
Normal file
1061
client/src/i18n/translations/es.js
Normal file
File diff suppressed because it is too large
Load Diff
1
client/src/i18n/translations/es.ts
Normal file
1
client/src/i18n/translations/es.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './es.js'
|
||||
Reference in New Issue
Block a user