mirror of
https://github.com/zen-browser/www.git
synced 2025-07-13 11:45:31 +02:00
155 lines
4.9 KiB
TypeScript
155 lines
4.9 KiB
TypeScript
import type { GetStaticPaths } from 'astro'
|
|
import { CONSTANT } from '~/constants'
|
|
import UI_EN from '~/i18n/en/translation.json'
|
|
|
|
/**
|
|
* Represents the available locales in the application
|
|
* @typedef {string} Locale
|
|
*/
|
|
export type Locale = (typeof locales)[number]
|
|
|
|
/**
|
|
* Generates a localized path by prefixing the locale if necessary
|
|
* @param {Locale} [locale] - The current locale
|
|
* @returns {function(string): string} A function that transforms paths based on the locale
|
|
*/
|
|
export const getPath = (locale?: Locale) => (path: string) => {
|
|
if (locale && locale !== CONSTANT.I18N.DEFAULT_LOCALE && !path.startsWith(`/${locale}`)) {
|
|
return `/${locale}${path.startsWith('/') ? '' : '/'}${path}`
|
|
}
|
|
return path
|
|
}
|
|
|
|
/**
|
|
* Extracts the current locale from Astro's params, defaulting to the default locale
|
|
* @param {Object} Astro - Astro's context object
|
|
* @param {Object} [Astro.params] - Routing parameters
|
|
* @param {string} [Astro.params.locale] - The current locale parameter
|
|
* @returns {Locale} The determined locale
|
|
*/
|
|
export const getLocale = (Astro: { params?: { locale?: string } }) => {
|
|
if (Astro.params?.locale) {
|
|
return Astro.params.locale as Locale
|
|
}
|
|
return CONSTANT.I18N.DEFAULT_LOCALE as Locale
|
|
}
|
|
|
|
/**
|
|
* List of all supported locales
|
|
* @type {Locale[]}
|
|
*/
|
|
export const locales = CONSTANT.I18N.LOCALES.map(({ value }) => value)
|
|
|
|
/**
|
|
* List of locales excluding the default locale
|
|
* @type {Locale[]}
|
|
*/
|
|
const otherLocales = CONSTANT.I18N.LOCALES.filter(({ value }) => value !== CONSTANT.I18N.DEFAULT_LOCALE)
|
|
|
|
/**
|
|
* Retrieves locales other than the default locale
|
|
* @returns {Locale[]} Array of non-default locales
|
|
*/
|
|
export const getOtherLocales = () => otherLocales
|
|
|
|
/**
|
|
* Type definition for UI translations based on the English translation
|
|
* @typedef {Object} UI
|
|
*/
|
|
export type UI = typeof UI_EN
|
|
|
|
/**
|
|
* Mapping of locales to their UI translation objects
|
|
* @type {Object.<Locale, UI>}
|
|
*/
|
|
export const ui = { en: UI_EN }
|
|
|
|
/**
|
|
* Retrieves UI translations for a given locale, merging with default translations
|
|
* @param {Locale} [locale] - The target locale for translations
|
|
* @returns {UI} Merged UI translations
|
|
*/
|
|
export const getUI = (locale?: Locale | string): UI => {
|
|
const validLocale = locales.includes(locale as Locale) ? locale : CONSTANT.I18N.DEFAULT_LOCALE
|
|
const defaultUI = ui[CONSTANT.I18N.DEFAULT_LOCALE]
|
|
const localeUI = ui[validLocale as Locale]
|
|
|
|
/**
|
|
* Recursively merges two objects, with the override object taking precedence
|
|
* @template T
|
|
* @param {T} defaultObj - The default object to merge from
|
|
* @param {Partial<T>} overrideObj - The object to merge over the default
|
|
* @returns {T} The deeply merged object
|
|
*/
|
|
function deepMerge<T extends object>(defaultObj: T, overrideObj: Partial<T>): T {
|
|
// Handle non-object cases
|
|
if (typeof defaultObj !== 'object' || defaultObj === null) {
|
|
return (overrideObj ?? defaultObj) as T
|
|
}
|
|
if (typeof overrideObj !== 'object' || overrideObj === null) {
|
|
return (overrideObj ?? defaultObj) as T
|
|
}
|
|
|
|
// Create a new object or array based on the default object's type
|
|
const result = Array.isArray(defaultObj) ? [...defaultObj] : { ...defaultObj }
|
|
|
|
// Merge properties from the default object
|
|
for (const key of Object.keys(defaultObj) as Array<keyof T>) {
|
|
const defaultValue = defaultObj[key]
|
|
const overrideValue = overrideObj[key]
|
|
|
|
// Recursively merge nested objects
|
|
if (
|
|
defaultValue !== null &&
|
|
overrideValue !== null &&
|
|
typeof defaultValue === 'object' &&
|
|
typeof overrideValue === 'object'
|
|
) {
|
|
// Type assertion to handle nested merging
|
|
;(result as Record<keyof T, unknown>)[key] = deepMerge(defaultValue as object, overrideValue as Partial<object>)
|
|
} else if (overrideValue !== undefined) {
|
|
// Override with the new value if it exists
|
|
;(result as Record<keyof T, unknown>)[key] = overrideValue
|
|
}
|
|
}
|
|
|
|
// Add any new properties from overrideObj
|
|
for (const key of Object.keys(overrideObj) as Array<keyof T>) {
|
|
if (!(key in defaultObj)) {
|
|
;(result as Record<keyof T, unknown>)[key] = overrideObj[key]
|
|
}
|
|
}
|
|
|
|
return result as T
|
|
}
|
|
|
|
return deepMerge(defaultUI, localeUI)
|
|
}
|
|
|
|
/**
|
|
* Generates static paths for internationalization
|
|
* @type {GetStaticPaths}
|
|
* @returns {Array} An array of static paths for different locales
|
|
*/
|
|
export const getStaticPaths = (() => {
|
|
return [
|
|
{
|
|
params: { locale: undefined },
|
|
props: { locale: CONSTANT.I18N.DEFAULT_LOCALE },
|
|
},
|
|
...CONSTANT.I18N.LOCALES.filter(({ value }) => value !== CONSTANT.I18N.DEFAULT_LOCALE).map(({ value }) => ({
|
|
params: { locale: value },
|
|
props: {
|
|
locale: value,
|
|
},
|
|
})),
|
|
]
|
|
}) satisfies GetStaticPaths
|
|
|
|
/**
|
|
* Retrieves all available locales, including both default and non-default
|
|
* @returns {Locale[]} Combined array of all locales
|
|
*/
|
|
export const getLocales = () => {
|
|
return [...locales, ...otherLocales]
|
|
}
|