1
0
Fork 0
mirror of https://github.com/Chocobozzz/PeerTube.git synced 2025-10-03 17:59:37 +02:00
Peertube/server/core/helpers/i18n.ts
Gianantonio Pini e74bf8ae2a
feat(config): add admin options to customize default "Browse videos" behaviour (#7193)
* feat(customBrowseVideosDefaultSort): update server config

* feat(customBrowseVideosDefaultSort): update client admin-config-general component

* feat(customBrowseVideosDefaultSort): add new consistency check to server checker-after-init

* feat(customBrowseVideosDefaultSort): update config .yaml with more details about available options

* feat(customBrowseVideosDefaultSort): refactor consistency check in server checker-after-init

* feat(customBrowseVideosDefaultSort): client, add new select-videos-sort shared component

* feat(customBrowseVideosDefaultSort): client, refactor admin-config-general component to use new select-videos-sort shared component

* feat(customBrowseVideosDefaultSort): client, fix my-select-videos-sort width in admin-config-general

* feat(customBrowseVideosDefaultSort): client, update video-filters-header scss

* feat(customBrowseVideosDefaultSort): client, update videos-list-all component

* feat(config): refactor checkBrowseVideosConfig logic into separate custom validator

* feat(config): refactor isBrowseVideosDefaultSortValid to use template literals

* feat(config): refactor isBrowseVideosDefaultSortValid

* feat(config): add check for invalid browse videos config to customConfigUpdateValidator

* feat(config): group browse-videos tests in describe block

* feat(config): refactor to use client.browse_videos section, instead of browse.videos section

* feat(config): add browse_videos default_scope config key (config and server changes)

* feat(config): add browse_videos default_scope config key (client changes)

* Reorder browse videos before videos

* Fix i18n message

---------

Co-authored-by: Chocobozzz <me@florianbigard.com>
2025-09-10 10:41:45 +02:00

88 lines
3 KiB
TypeScript

import { AVAILABLE_LOCALES, getCompleteLocale } from '@peertube/peertube-core-utils'
import { CONFIG } from '@server/initializers/config.js'
import { LANGUAGE_COOKIE_NAME, LANGUAGE_HEADER, SERVER_INTERNAL_LOCALES_BASE_PATH } from '@server/initializers/constants.js'
import { MUser } from '@server/types/models/index.js'
import Bluebird from 'bluebird'
import express from 'express'
import { readJson } from 'fs-extra/esm'
import { readdir } from 'fs/promises'
import i18next from 'i18next'
import ICU from 'i18next-icu'
import { join } from 'path'
import { logger } from './logger.js'
export async function initI18n () {
const locales = await readdir(SERVER_INTERNAL_LOCALES_BASE_PATH)
const resources: Record<string, Record<string, string>> = {}
await Bluebird.map(locales, async locale => {
const localePath = join(SERVER_INTERNAL_LOCALES_BASE_PATH, locale)
const translation = await readJson(join(localePath, 'translation.json'))
resources[locale] = { translation }
}, { concurrency: 10 })
return i18next.use(ICU)
.init({
resources,
nsSeparator: false,
keySeparator: false,
// do not load a fallback
fallbackLng: false
})
.then(() => logger.info('i18n initialized with locales: ' + Object.keys(resources).join(', ')))
}
// ---------------------------------------------------------------------------
export type TranslateFn = (key: string, context: Record<string, string | number>) => string
export function useI18n (req: express.Request, res: express.Response, next: express.NextFunction) {
req.t = (key: string, context: Record<string, string | number> = {}) => {
// Use req special header language
// Or user language
// Or default language
const language = guessLanguageFromReq(req, res)
return t(key, language, context)
}
next()
}
export function guessLanguageFromReq (req: express.Request, res: express.Response) {
return req.headers[LANGUAGE_HEADER] as string ||
res.locals.oauth?.token?.User?.language ||
req.acceptsLanguages(AVAILABLE_LOCALES) ||
CONFIG.INSTANCE.DEFAULT_LANGUAGE
}
export function t (key: string, language: string, context: Record<string, string | number> = {}) {
if (!language) throw new Error('Language is required for translation')
return i18next.t(key, { lng: getCompleteLocale(language), ...context })
}
export function tu (key: string, user: Pick<MUser, 'language' | 'getLanguage'>, context: Record<string, string | number> = {}) {
return t(key, user.getLanguage(), context)
}
// ---------------------------------------------------------------------------
export function setClientLanguageCookie (res: express.Response, language: string) {
if (language === null) {
res.clearCookie(LANGUAGE_COOKIE_NAME)
return
}
res.cookie(LANGUAGE_COOKIE_NAME, language, {
secure: true,
sameSite: 'none',
maxAge: 1000 * 3600 * 24 * 90 // 3 months
})
}
// ---------------------------------------------------------------------------
export const englishLanguage = 'en-US'