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:
14
node_modules/i18next-http-backend/lib/getFetch.cjs
generated
vendored
Normal file
14
node_modules/i18next-http-backend/lib/getFetch.cjs
generated
vendored
Normal 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
169
node_modules/i18next-http-backend/lib/index.js
generated
vendored
Normal 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
171
node_modules/i18next-http-backend/lib/request.js
generated
vendored
Normal 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
44
node_modules/i18next-http-backend/lib/utils.js
generated
vendored
Normal 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)
|
||||
}
|
Reference in New Issue
Block a user