@samitouri / QOSami-HFS / commits / 5197e5d8

plugins: HFS.customizeText

Massimo Melina committed Dec 7, 2025 at 15:26 UTC 5197e5d83cf57c6ad2dcef5c21f1461b627aca6c
4 files changed +21 -6
dev-plugins.md
+6 -1
@@ -456,6 +456,9 @@ In frontend you will have access to the `HFS` object of the global scope, which
456 - `markVideoComponent(Component): Component` if you replace the show-video component with yours, wrap it with this function.
457 - `isAudioComponent(Component): boolean` tell if the component is used by show for audio files.
458 - `markAudioComponent(Component): Component` if you replace the show-audio component with yours, wrap it with this function.
459 +- `customizeText(changes: { [key]: string }, languageCode?: string)` customize text. Find text keys to change, and assign new text.
460 + If you don't specify a languageCode, the change will apply to all languages (and has precedence).
461 + Find they keys in this file https://github.com/rejetto/hfs/blob/main/src/langs/hfs-lang-en.json (english language).
462
463 - The following properties are accessible only immediately at top-level; don't call it later in a callback.
464 - `getPluginConfig()` returns object of all config keys that are declared frontend-accessible by this plugin.
@@ -1111,4 +1114,6 @@ If you want to override a text regardless of the language, use the special langu
1114 - HFS.userBelongsTo now supports array of usernames
1115 - 12.96 (v0.57.27)
1116 - frontend events: loginOk, loginFailed
1114 - - api.normalizeFilename
\ No newline at end of file
1117 + - api.normalizeFilename
1118 +- 12.97 (v0.57.28)
1119 + - HFS.customizeText
\ No newline at end of file
frontend/src/misc.ts
+8 -1
@@ -16,6 +16,7 @@ import { logout } from './login'
16 import { subscribeKey } from 'valtio/utils'
17 import { uploadState } from './uploadQueue'
18 import { fileShow, Video, Audio, getShowComponent } from './show'
19 +import { ANY_LANGUAGE } from '../../src/i18n'
20 import { debounceAsync } from '../../src/debounceAsync'
21 export * from '@hfs/shared'
22 import i18n from './i18n'
@@ -102,7 +103,7 @@ export function formatTimestamp(x: number | string | Date, options?: Intl.DateTi
103 import * as thisModule from './misc'
104 Object.assign(getHFS(), {
105 h, React, state, t, _, dialogLib, apiCall, useApi, reloadList, logout, Icon, hIcon, iconBtn, useBatch, fileShow,
105 - toast, domOn, getNotifications, debounceAsync, useSnapState, DirEntry, Btn,
106 + toast, domOn, getNotifications, debounceAsync, useSnapState, DirEntry, Btn, i18n,
107 fileShowComponents: { Video, Audio },
108 isVideoComponent, markVideoComponent, isAudioComponent, markAudioComponent,
109 isShowSupported: getShowComponent,
@@ -127,6 +128,12 @@ Object.assign(getHFS(), {
128 try { return _.find(state.list, { uri: new URL(a.href).pathname }) }
129 catch {}
130 },
131 + customizeText(mods: Dict<string>, lang=ANY_LANGUAGE) {
132 + const o = i18n.state.translations
133 + if (lang === ANY_LANGUAGE || lang === cross.EMBEDDED_LANGUAGE) // these are missing at the start, but we need them if we want to customize content
134 + o[lang] ??= { translate: {} }
135 + Object.assign(o[lang]?.translate || {}, mods)
136 + }
137 })
138
139 markVideoComponent(Video)
src/const.ts
+1 -1
@@ -9,7 +9,7 @@ import { formatTimestamp } from './cross'
9 import { argv } from './argv'
10 export * from './cross-const'
11
12 -export const API_VERSION = 12.96
12 +export const API_VERSION = 12.97
13 export const COMPATIBLE_API_VERSION = 1 // while changes in the api are not breaking, this number stays the same, otherwise it is made equal to API_VERSION
14
15 // you can add arguments with this file, currently used for the update process on mac/linux
src/i18n.ts
+6 -3
@@ -3,6 +3,9 @@
3 import { proxy, useSnapshot } from 'valtio'
4 import { watch } from 'valtio/utils'
5
6 +export const ANY_LANGUAGE = 'all'
7 +
8 +// this code is in the backend to give the chance to plugins to translate http's body
9 export function i18nFromTranslations(translations: Record<string, any>, embedded='en') {
10 const state = proxy({
11 embedded,
@@ -11,8 +14,8 @@ export function i18nFromTranslations(translations: Record<string, any>, embedded
14 })
15 const searchLangs: string[] = []
16 watch(get => {
14 - const snapshot = get(state)
15 - searchLangs.splice(0, Infinity, 'all', ...Object.keys(snapshot.translations), snapshot.embedded) // replace completely
17 + const snap = get(state)
18 + searchLangs.splice(0, Infinity, ANY_LANGUAGE, ...snap.disabled ? [] : Object.keys(snap.translations), snap.embedded) // replace the array completely
19 })
20
21 const warns = new Set() // avoid duplicates
@@ -36,7 +39,7 @@ export function i18nFromTranslations(translations: Record<string, any>, embedded
39 let selectedLang = '' // keep track of where we find the translation
40 const { embedded } = state
41 const langs = Object.keys(state.translations)
39 - if (!state.disabled) for (const key of keys) {
42 + for (const key of keys) {
43 for (const lang of searchLangs)
44 if (found = state.translations[selectedLang=lang]?.translate?.[key]) break
45 if (found) break