better code: moved language logic server-side

Massimo Melina committed Apr 5, 2023 at 10:46 UTC 83252315f6bb57ca8b168ab7670751789f4c431b
4 files changed +31 -38
frontend/src/i18n.ts
+2 -9
@@ -14,16 +14,9 @@ export function useI18N() {
14 }
15
16 export function I18Nprovider({ embedded='en', ...props }) {
17 - const langs = urlParams.lang?.split(',') || navigator.languages
17 + loaded = getHFS().lang
18 state.embedded = embedded
19 - let all = useApi(!getHFS().lang && langs[0] !== embedded && 'load_lang', { embedded, lang: langs }, { noModal: true })
20 - ?? getHFS().lang
21 - useEffect(() => {
22 - if (all instanceof Error)
23 - all = undefined
24 - state.langs = Object.keys(all||{})
25 - loaded = all
26 - }, [all])
19 + state.langs = Object.keys(loaded||{})
20 return h(Fragment, props)
21 }
22
src/api.lang.ts
+25 -3
@@ -5,12 +5,14 @@ import _ from 'lodash'
5 import glob from 'fast-glob'
6 import { readFile, rm, writeFile } from 'fs/promises'
7 import { HTTP_BAD_REQUEST, HTTP_NOT_ACCEPTABLE, HTTP_SERVER_ERROR } from './const'
8 -import { tryJson } from './misc'
8 +import { tryJson, wantArray } from './misc'
9 import { defineConfig } from './config'
10 import { watchLoad } from './watchLoad'
11 +import Koa from 'koa'
12
13 const PREFIX = 'hfs-lang-'
14 const SUFFIX = '.json'
15 +const EMBEDDED = 'en'
16
17 const apis: ApiHandlers = {
18
@@ -69,8 +71,28 @@ function validateCode(code: string) {
71 throw new ApiError(HTTP_BAD_REQUEST, 'bad code/filename')
72 }
73
72 -export function getForceLangData() {
73 - return forceLangData
74 +export async function getLangData(ctx: Koa.Context) {
75 + if (forceLangData)
76 + return forceLangData
77 + const ret: any = {}
78 + const csv = String(ctx.query.lang||'') || ctx.get('Accept-Language') || ''
79 + const langs = wantArray(csv.split(',').map(x => x.toLowerCase()))
80 + let i = 0
81 + while (i < langs.length) {
82 + let k = langs[i] || '' // shut up ts
83 + if (!k || k === EMBEDDED) break
84 + try { ret[k!] = JSON.parse(await readFile(`hfs-lang-${k}.json`, 'utf8')) }
85 + catch {
86 + do { k = k.substring(0, k.lastIndexOf('-'))
87 + } while (k && langs.includes(k))
88 + if (k) {
89 + langs[i] = k // overwrite and retry
90 + continue
91 + }
92 + }
93 + i++
94 + }
95 + return ret
96 }
97
98 let forceLangData: any
src/frontEndApis.ts
+1 -23
@@ -16,9 +16,8 @@ import {
16 HTTP_UNAUTHORIZED
17 } from './const'
18 import { hasPermission, urlToNode } from './vfs'
19 -import { mkdir, readFile, rm } from 'fs/promises'
19 +import { mkdir, rm } from 'fs/promises'
20 import { join } from 'path'
21 -import { wantArray } from './misc'
21
22 export const customHeader = defineConfig('custom_header', '')
23
@@ -73,27 +72,6 @@ export const frontEndApis: ApiHandlers = {
72 }
73 },
74
76 - async load_lang({ lang, embedded }) {
77 - const ret: any = {}
78 - const langs = wantArray(lang).map(x => x.toLowerCase())
79 - let i = 0
80 - while (i < langs.length) {
81 - let x = langs[i]
82 - if (x === embedded) break
83 - try { ret[x] = JSON.parse(await readFile(`hfs-lang-${x}.json`, 'utf8')) }
84 - catch {
85 - do { x = x.substring(0, x.lastIndexOf('-'))
86 - } while (x && langs.includes(x))
87 - if (x) {
88 - langs[i] = x // overwrite and retry
89 - continue
90 - }
91 - }
92 - i++
93 - }
94 - return ret
95 - }
96 -
75 }
76
77 export function notifyClient(ctx: Koa.Context, name: string, data: any) {
src/serveGuiFiles.ts
+3 -3
@@ -23,8 +23,7 @@ import { subscribe } from 'valtio'
23 import { customHtmlState, getSection } from './customHtml'
24 import _ from 'lodash'
25 import { defineConfig } from './config'
26 -import { watchLoad } from './watchLoad'
27 -import { getForceLangData } from './api.lang'
26 +import { getLangData } from './api.lang'
27
28 // in case of dev env we have our static files within the 'dist' folder'
29 const DEV_STATIC = process.env.DEV ? 'dist/' : ''
@@ -90,6 +89,7 @@ async function treatIndex(ctx: Koa.Context, filesUri: string, body: string) {
89 configs = getPluginInfo(name).onFrontendConfig?.(configs) || configs
90 return !_.isEmpty(configs) && [name, configs]
91 })))
92 + const lang = await getLangData(ctx)
93 let ret = body
94 .replace(/((?:src|href) *= *['"])\/?(?![a-z]+:\/\/)/g, '$1' + ctx.state.revProxyPath + filesUri)
95 .replace('</head>', () => `
@@ -108,7 +108,7 @@ async function treatIndex(ctx: Koa.Context, filesUri: string, body: string) {
108 customHtml: _.omit(Object.fromEntries(customHtmlState.sections),
109 ['top','bottom']), // excluding sections we apply in this phase
110 fileMenuOnLink: fileMenuOnLink.get(),
111 - lang: getForceLangData()
111 + lang
112 }, null, 4)
113 .replace(/<(\/script)/g, '<"+"$1') /*avoid breaking our script container*/}
114 document.documentElement.setAttribute('ver', '${VERSION.split('-')[0] /*for style selectors*/}')