fix: translation

This commit is contained in:
Tobias Klemp
2025-12-03 20:52:19 +01:00
parent 42080520bf
commit 2806575233
8 changed files with 262 additions and 11 deletions

View File

@@ -0,0 +1,103 @@
import type { TolgeeStaticData } from '@tolgee/svelte'
import { env } from '$env/dynamic/private'
import { TOLGEE_API_KEY, TOLGEE_API_URL } from '$env/static/private'
import { loadAsync } from 'jszip'
class TranslationCacheItem {
constructor(
public translastions: TolgeeStaticData,
public timestamp: number,
) {}
get isExpired(): boolean {
const now = Date.now()
const TTL = Number(env.PRIVATE_TRANSLATION_CACHE_TTL)
if (Number.isNaN(TTL)) return false
return now - this.timestamp > TTL * 1000
}
}
class LanguageService {
protected cache: Record<string, TranslationCacheItem> = {}
async getTranslations(
languageCodes: string[],
): Promise<Record<string, TolgeeStaticData>> {
const languageCodesToFetch = new Set<string>()
const result: Record<string, TolgeeStaticData> = {}
for (const lang of languageCodes) {
const cacheItem = this.cache[lang]
if (!cacheItem || cacheItem.isExpired) {
languageCodesToFetch.add(lang)
continue
}
Object.assign(result, { [lang]: cacheItem.translastions })
}
if (languageCodesToFetch.size === 0) {
return result
}
const freshTranslations = await this.fetchTranslations(
Array.from(languageCodesToFetch),
)
for (const langKey of Object.keys(freshTranslations)) {
if (langKey in this.cache) {
delete this.cache[langKey]
}
this.cache[langKey] = new TranslationCacheItem(
freshTranslations[langKey],
Date.now(),
)
result[langKey] = Object.assign(freshTranslations[langKey])
}
return result
}
protected async fetchTranslations(languageCodes: string | string[]) {
const url = new URL(`${TOLGEE_API_URL}/v2/projects/2/export`)
url.searchParams.append('format', 'JSON')
url.searchParams.append('zip', 'true')
url.searchParams.append('supportArrays', 'true')
for (const lang of languageCodes) {
url.searchParams.append('languages', lang)
}
const response = await fetch(url, {
method: 'GET',
headers: {
'X-API-Key': TOLGEE_API_KEY,
Accept: 'application/*',
},
})
const zippedBuffer = await response.arrayBuffer()
const zip = await loadAsync(zippedBuffer)
const translations: Record<string, TolgeeStaticData> = {}
for (const [filename, file] of Object.entries(zip.files)) {
if (!file.dir && filename.endsWith('.json')) {
const content = await file.async('string')
const locale = filename.replace('.json', '')
translations[locale] = JSON.parse(content)
}
}
return translations
}
}
export const languageService = new LanguageService()

View File

@@ -0,0 +1,7 @@
import { languageService } from '$lib/server/LanguageService'
import type { LayoutServerLoad } from './$types'
export const load: LayoutServerLoad = async () => {
const translations = await languageService.getTranslations(['de'])
return translations
}

View File

@@ -6,8 +6,25 @@
Tolgee,
DevTools,
FormatSimple,
type TolgeeStaticData,
} from '@tolgee/svelte'
import { TopBar } from '$lib/components/ui/topBar'
import {
PUBLIC_TOLGEE_API_KEY,
PUBLIC_TOLGEE_API_URL,
} from '$env/static/public'
import type { Snippet } from 'svelte'
let { children, data }: { data: TolgeeStaticData; children: Snippet } =
$props()
function getLang(lang: string) {
return async (): Promise<Record<string, any>> => {
const res = await fetch(`/lang/${lang}`)
const translations = await res.json()
return translations
}
}
const tolgee = Tolgee()
.use(DevTools())
@@ -15,15 +32,15 @@
.init({
language: 'de',
// for development
apiUrl: import.meta.env.VITE_TOLGEE_API_URL,
apiKey: import.meta.env.VITE_TOLGEE_API_KEY,
// // for development
// apiUrl: PUBLIC_TOLGEE_API_URL,
// apiKey: PUBLIC_TOLGEE_API_KEY,
// for production
staticData: {},
staticData: {
de: data['de'] ?? getLang('de'),
},
})
let { children } = $props()
</script>
<svelte:head>

View File

@@ -0,0 +1,10 @@
import { json } from '@sveltejs/kit'
import { languageService } from '$lib/server/LanguageService.js'
export async function GET({ params }) {
const { language } = params
const result = await languageService.getTranslations([language])
return json(result[language])
}