Add internationalization support with astro-i18next integration

- Implemented astro-i18next for multi-language support, including English, Dutch, and Italian.
- Configured default locale and language fallback settings.
- Defined routes for localized content in the configuration.
- Updated package.json and package-lock.json to include new dependencies for i18next and related plugins.
This commit is contained in:
becarta
2025-05-23 15:10:00 +02:00
parent 8a3507dce0
commit 3168826fa8
581 changed files with 88691 additions and 494 deletions

14
node_modules/i18next-http-backend/lib/getFetch.cjs generated vendored Normal file
View File

@@ -0,0 +1,14 @@
/* eslint-disable no-var, no-undef */
var fetchApi = typeof fetch === 'function' ? fetch : undefined
if (typeof global !== 'undefined' && global.fetch) {
fetchApi = global.fetch
} else if (typeof window !== 'undefined' && window.fetch) {
fetchApi = window.fetch
}
if (typeof require !== 'undefined' && typeof window === 'undefined') {
var f = fetchApi || require('cross-fetch')
if (f.default) f = f.default
exports.default = f
module.exports = exports.default
}

169
node_modules/i18next-http-backend/lib/index.js generated vendored Normal file
View File

@@ -0,0 +1,169 @@
import { makePromise } from './utils.js'
import request from './request.js'
const getDefaults = () => {
return {
loadPath: '/locales/{{lng}}/{{ns}}.json',
addPath: '/locales/add/{{lng}}/{{ns}}',
parse: data => JSON.parse(data),
stringify: JSON.stringify,
parsePayload: (namespace, key, fallbackValue) => ({ [key]: fallbackValue || '' }),
parseLoadPayload: (languages, namespaces) => undefined,
request,
reloadInterval: typeof window !== 'undefined' ? false : 60 * 60 * 1000,
customHeaders: {},
queryStringParams: {},
crossDomain: false, // used for XmlHttpRequest
withCredentials: false, // used for XmlHttpRequest
overrideMimeType: false, // used for XmlHttpRequest
requestOptions: { // used for fetch
mode: 'cors',
credentials: 'same-origin',
cache: 'default'
}
}
}
class Backend {
constructor (services, options = {}, allOptions = {}) {
this.services = services
this.options = options
this.allOptions = allOptions
this.type = 'backend'
this.init(services, options, allOptions)
}
init (services, options = {}, allOptions = {}) {
this.services = services
this.options = { ...getDefaults(), ...(this.options || {}), ...options }
this.allOptions = allOptions
if (this.services && this.options.reloadInterval) {
const timer = setInterval(() => this.reload(), this.options.reloadInterval)
if (typeof timer === 'object' && typeof timer.unref === 'function') timer.unref()
}
}
readMulti (languages, namespaces, callback) {
this._readAny(languages, languages, namespaces, namespaces, callback)
}
read (language, namespace, callback) {
this._readAny([language], language, [namespace], namespace, callback)
}
_readAny (languages, loadUrlLanguages, namespaces, loadUrlNamespaces, callback) {
let loadPath = this.options.loadPath
if (typeof this.options.loadPath === 'function') {
loadPath = this.options.loadPath(languages, namespaces)
}
loadPath = makePromise(loadPath)
loadPath.then(resolvedLoadPath => {
if (!resolvedLoadPath) return callback(null, {})
const url = this.services.interpolator.interpolate(resolvedLoadPath, { lng: languages.join('+'), ns: namespaces.join('+') })
this.loadUrl(url, callback, loadUrlLanguages, loadUrlNamespaces)
})
}
loadUrl (url, callback, languages, namespaces) {
const lng = (typeof languages === 'string') ? [languages] : languages
const ns = (typeof namespaces === 'string') ? [namespaces] : namespaces
// parseLoadPayload — default undefined
const payload = this.options.parseLoadPayload(lng, ns)
this.options.request(this.options, url, payload, (err, res) => {
if (res && ((res.status >= 500 && res.status < 600) || !res.status)) return callback('failed loading ' + url + '; status code: ' + res.status, true /* retry */)
if (res && res.status >= 400 && res.status < 500) return callback('failed loading ' + url + '; status code: ' + res.status, false /* no retry */)
if (!res && err && err.message) {
const errorMessage = err.message.toLowerCase()
// for example:
// Chrome: "Failed to fetch"
// Firefox: "NetworkError when attempting to fetch resource."
// Safari: "Load failed"
const isNetworkError = [
'failed',
'fetch',
'network',
'load'
].find((term) => errorMessage.indexOf(term) > -1)
if (isNetworkError) {
return callback('failed loading ' + url + ': ' + err.message, true /* retry */)
}
}
if (err) return callback(err, false)
let ret, parseErr
try {
if (typeof res.data === 'string') {
ret = this.options.parse(res.data, languages, namespaces)
} else { // fallback, which omits calling the parse function
ret = res.data
}
} catch (e) {
parseErr = 'failed parsing ' + url + ' to json'
}
if (parseErr) return callback(parseErr, false)
callback(null, ret)
})
}
create (languages, namespace, key, fallbackValue, callback) {
// If there is a falsey addPath, then abort -- this has been disabled.
if (!this.options.addPath) return
if (typeof languages === 'string') languages = [languages]
const payload = this.options.parsePayload(namespace, key, fallbackValue)
let finished = 0
const dataArray = []
const resArray = []
languages.forEach(lng => {
let addPath = this.options.addPath
if (typeof this.options.addPath === 'function') {
addPath = this.options.addPath(lng, namespace)
}
const url = this.services.interpolator.interpolate(addPath, { lng, ns: namespace })
this.options.request(this.options, url, payload, (data, res) => {
// TODO: if res.status === 4xx do log
finished += 1
dataArray.push(data)
resArray.push(res)
if (finished === languages.length) {
if (typeof callback === 'function') callback(dataArray, resArray)
}
})
})
}
reload () {
const { backendConnector, languageUtils, logger } = this.services
const currentLanguage = backendConnector.language
if (currentLanguage && currentLanguage.toLowerCase() === 'cimode') return // avoid loading resources for cimode
const toLoad = []
const append = (lng) => {
const lngs = languageUtils.toResolveHierarchy(lng)
lngs.forEach(l => {
if (toLoad.indexOf(l) < 0) toLoad.push(l)
})
}
append(currentLanguage)
if (this.allOptions.preload) this.allOptions.preload.forEach((l) => append(l))
toLoad.forEach(lng => {
this.allOptions.ns.forEach(ns => {
backendConnector.read(lng, ns, 'read', null, null, (err, data) => {
if (err) logger.warn(`loading namespace ${ns} for language ${lng} failed`, err)
if (!err && data) logger.log(`loaded namespace ${ns} for language ${lng}`, data)
backendConnector.loaded(`${lng}|${ns}`, err, data)
})
})
})
}
}
Backend.type = 'backend'
export default Backend

171
node_modules/i18next-http-backend/lib/request.js generated vendored Normal file
View File

@@ -0,0 +1,171 @@
import { hasXMLHttpRequest } from './utils.js'
import * as fetchNode from './getFetch.cjs'
let fetchApi = typeof fetch === 'function' ? fetch : undefined
if (typeof global !== 'undefined' && global.fetch) {
fetchApi = global.fetch
} else if (typeof window !== 'undefined' && window.fetch) {
fetchApi = window.fetch
}
let XmlHttpRequestApi
if (hasXMLHttpRequest()) {
if (typeof global !== 'undefined' && global.XMLHttpRequest) {
XmlHttpRequestApi = global.XMLHttpRequest
} else if (typeof window !== 'undefined' && window.XMLHttpRequest) {
XmlHttpRequestApi = window.XMLHttpRequest
}
}
let ActiveXObjectApi
if (typeof ActiveXObject === 'function') {
if (typeof global !== 'undefined' && global.ActiveXObject) {
ActiveXObjectApi = global.ActiveXObject
} else if (typeof window !== 'undefined' && window.ActiveXObject) {
ActiveXObjectApi = window.ActiveXObject
}
}
if (!fetchApi && fetchNode && !XmlHttpRequestApi && !ActiveXObjectApi) fetchApi = fetchNode.default || fetchNode // because of strange export
if (typeof fetchApi !== 'function') fetchApi = undefined
const addQueryString = (url, params) => {
if (params && typeof params === 'object') {
let queryString = ''
// Must encode data
for (const paramName in params) {
queryString += '&' + encodeURIComponent(paramName) + '=' + encodeURIComponent(params[paramName])
}
if (!queryString) return url
url = url + (url.indexOf('?') !== -1 ? '&' : '?') + queryString.slice(1)
}
return url
}
const fetchIt = (url, fetchOptions, callback, altFetch) => {
const resolver = (response) => {
if (!response.ok) return callback(response.statusText || 'Error', { status: response.status })
response.text().then((data) => {
callback(null, { status: response.status, data })
}).catch(callback)
}
if (altFetch) {
// already checked to have the proper signature
const altResponse = altFetch(url, fetchOptions)
if (altResponse instanceof Promise) {
altResponse.then(resolver).catch(callback)
return
}
// fall through
}
if (typeof fetch === 'function') { // react-native debug mode needs the fetch function to be called directly (no alias)
fetch(url, fetchOptions).then(resolver).catch(callback)
} else {
fetchApi(url, fetchOptions).then(resolver).catch(callback)
}
}
let omitFetchOptions = false
// fetch api stuff
const requestWithFetch = (options, url, payload, callback) => {
if (options.queryStringParams) {
url = addQueryString(url, options.queryStringParams)
}
const headers = {
...(typeof options.customHeaders === 'function' ? options.customHeaders() : options.customHeaders)
}
if (typeof window === 'undefined' && typeof global !== 'undefined' && typeof global.process !== 'undefined' && global.process.versions && global.process.versions.node) {
headers['User-Agent'] = `i18next-http-backend (node/${global.process.version}; ${global.process.platform} ${global.process.arch})`
}
if (payload) headers['Content-Type'] = 'application/json'
const reqOptions = typeof options.requestOptions === 'function' ? options.requestOptions(payload) : options.requestOptions
const fetchOptions = {
method: payload ? 'POST' : 'GET',
body: payload ? options.stringify(payload) : undefined,
headers,
...(omitFetchOptions ? {} : reqOptions)
}
const altFetch = typeof options.alternateFetch === 'function' && options.alternateFetch.length >= 1 ? options.alternateFetch : undefined
try {
fetchIt(url, fetchOptions, callback, altFetch)
} catch (e) {
if (!reqOptions || Object.keys(reqOptions).length === 0 || !e.message || e.message.indexOf('not implemented') < 0) {
return callback(e)
}
try {
Object.keys(reqOptions).forEach((opt) => {
delete fetchOptions[opt]
})
fetchIt(url, fetchOptions, callback, altFetch)
omitFetchOptions = true
} catch (err) {
callback(err)
}
}
}
// xml http request stuff
const requestWithXmlHttpRequest = (options, url, payload, callback) => {
if (payload && typeof payload === 'object') {
// if (!cache) payload._t = Date.now()
// URL encoded form payload must be in querystring format
payload = addQueryString('', payload).slice(1)
}
if (options.queryStringParams) {
url = addQueryString(url, options.queryStringParams)
}
try {
let x
if (XmlHttpRequestApi) {
x = new XmlHttpRequestApi()
} else {
x = new ActiveXObjectApi('MSXML2.XMLHTTP.3.0')
}
x.open(payload ? 'POST' : 'GET', url, 1)
if (!options.crossDomain) {
x.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
}
x.withCredentials = !!options.withCredentials
if (payload) {
x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
}
if (x.overrideMimeType) {
x.overrideMimeType('application/json')
}
let h = options.customHeaders
h = typeof h === 'function' ? h() : h
if (h) {
for (const i in h) {
x.setRequestHeader(i, h[i])
}
}
x.onreadystatechange = () => {
x.readyState > 3 && callback(x.status >= 400 ? x.statusText : null, { status: x.status, data: x.responseText })
}
x.send(payload)
} catch (e) {
console && console.log(e)
}
}
const request = (options, url, payload, callback) => {
if (typeof payload === 'function') {
callback = payload
payload = undefined
}
callback = callback || (() => {})
if (fetchApi && url.indexOf('file:') !== 0) {
// use fetch api
return requestWithFetch(options, url, payload, callback)
}
if (hasXMLHttpRequest() || typeof ActiveXObject === 'function') {
// use xml http request
return requestWithXmlHttpRequest(options, url, payload, callback)
}
callback(new Error('No fetch and no xhr implementation found!'))
}
export default request

44
node_modules/i18next-http-backend/lib/utils.js generated vendored Normal file
View File

@@ -0,0 +1,44 @@
const arr = []
const each = arr.forEach
const slice = arr.slice
export function defaults (obj) {
each.call(slice.call(arguments, 1), (source) => {
if (source) {
for (const prop in source) {
if (obj[prop] === undefined) obj[prop] = source[prop]
}
}
})
return obj
}
export function hasXMLHttpRequest () {
return (typeof XMLHttpRequest === 'function' || typeof XMLHttpRequest === 'object')
}
/**
* Determine whether the given `maybePromise` is a Promise.
*
* @param {*} maybePromise
*
* @returns {Boolean}
*/
function isPromise (maybePromise) {
return !!maybePromise && typeof maybePromise.then === 'function'
}
/**
* Convert any value to a Promise than will resolve to this value.
*
* @param {*} maybePromise
*
* @returns {Promise}
*/
export function makePromise (maybePromise) {
if (isPromise(maybePromise)) {
return maybePromise
}
return Promise.resolve(maybePromise)
}