Skip to content
Merged
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
6 changes: 4 additions & 2 deletions packages/web-runtime/src/container/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ import {
onSSESpaceDeletedEvent,
onSSESpaceEnabledEvent
} from './sse'
import { loadAppTranslations } from '../helpers/language'
import { loadAppTranslations, resolveInitialLanguage } from '../helpers/language'
import { urlJoin } from '@opencloud-eu/web-client'
import { sha256 } from '@noble/hashes/sha2.js'
import { bytesToHex } from '@noble/hashes/utils.js'
Expand Down Expand Up @@ -440,7 +440,9 @@ export const announceGettext = ({
app: App
} & Partial<GetTextOptions>) => {
const gettext = createGettext({
defaultLanguage: navigator.language.substring(0, 2),
defaultLanguage: resolveInitialLanguage({
browserLanguage: navigator.language
}),
silent: true,
...options
})
Expand Down
59 changes: 51 additions & 8 deletions packages/web-runtime/src/helpers/language.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,64 @@ import { ApplicationInformation } from '@opencloud-eu/web-pkg'
import { merge } from 'lodash-es'
import { Language, Translations } from 'vue3-gettext'

export const currentLanguageLocalStorageKey = 'oc_language'

function normalizeLanguage(languageSetting: string): string {
const trimmed = languageSetting.trim()
if (!trimmed) {
return ''
}
return trimmed.includes('-') ? trimmed.split('-')[0] : trimmed
}

function getStoredLanguage(): string {
const storedLanguage = window.localStorage.getItem(currentLanguageLocalStorageKey) ?? ''
if (!storedLanguage) {
return ''
}
return normalizeLanguage(storedLanguage)
}

function storeLanguage(language: string): void {
window.localStorage.setItem(currentLanguageLocalStorageKey, language)
}

function setDocumentLanguage(languageSetting: string): void {
const currentLanguage = normalizeLanguage(languageSetting)
if (!currentLanguage) {
return
}

document.documentElement.lang = currentLanguage
}

export const resolveInitialLanguage = ({
Comment thread
AlexAndBear marked this conversation as resolved.
browserLanguage
}: {
browserLanguage: string
}): string => {
const stored = getStoredLanguage()
const currentLanguage = stored || normalizeLanguage(browserLanguage) || 'en'

setDocumentLanguage(currentLanguage)
return currentLanguage
}

export const setCurrentLanguage = ({
language,
languageSetting = null
}: {
language: Language
languageSetting?: string
languageSetting?: string | null
}): void => {
let currentLanguage = languageSetting
if (currentLanguage) {
if (currentLanguage.indexOf('-')) {
currentLanguage = currentLanguage.split('-')[0]
}
language.current = currentLanguage
document.documentElement.lang = currentLanguage
const currentLanguage = normalizeLanguage(languageSetting || language.current)
if (!currentLanguage) {
return
}

language.current = currentLanguage
setDocumentLanguage(currentLanguage)
storeLanguage(currentLanguage)
}

/**
Expand Down
71 changes: 71 additions & 0 deletions packages/web-runtime/tests/unit/helpers/language.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { mock } from 'vitest-mock-extended'
import { nextTick } from 'vue'
import type { Language } from 'vue3-gettext'
import {
currentLanguageLocalStorageKey,
resolveInitialLanguage,
setCurrentLanguage
} from '../../../src/helpers/language'

describe('language helpers', () => {
beforeEach(() => {
window.localStorage.clear()
document.documentElement.lang = ''
})

it('prefers stored language over browser language', () => {
window.localStorage.setItem(currentLanguageLocalStorageKey, 'en')

const lang = resolveInitialLanguage({
browserLanguage: 'de-DE'
})

expect(lang).toBe('en')
expect(document.documentElement.lang).toBe('en')
})

it('falls back to browser language when no stored language exists', () => {
const lang = resolveInitialLanguage({
browserLanguage: 'de-DE'
})

expect(lang).toBe('de')
expect(document.documentElement.lang).toBe('de')
})

it('falls back to english when browser language is missing', () => {
const lang = resolveInitialLanguage({
browserLanguage: ''
})

expect(lang).toBe('en')
expect(document.documentElement.lang).toBe('en')
})

it('sets, normalizes and stores the selected language', async () => {
const language = mock<Language>({ current: 'de' })

setCurrentLanguage({
language,
languageSetting: 'en-US'
})
await nextTick()

expect(language.current).toBe('en')
expect(document.documentElement.lang).toBe('en')
expect(window.localStorage.getItem(currentLanguageLocalStorageKey)).toBe('en')
})

it('sets and stores current language when language setting is not provided', async () => {
const language = mock<Language>({ current: 'fr-FR' })

setCurrentLanguage({
language
})
await nextTick()

expect(language.current).toBe('fr')
expect(document.documentElement.lang).toBe('fr')
expect(window.localStorage.getItem(currentLanguageLocalStorageKey)).toBe('fr')
})
})