mirror of
https://github.com/zen-browser/www.git
synced 2025-07-07 17:05:32 +02:00
parent
cb4df21583
commit
054b55cc4e
20 changed files with 493 additions and 1584 deletions
|
@ -9,7 +9,7 @@ export default defineConfig({
|
||||||
site: 'https://zen-browser.app',
|
site: 'https://zen-browser.app',
|
||||||
i18n: {
|
i18n: {
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
locales: ['en', 'ja', 'es'],
|
locales: ['en', 'ja'],
|
||||||
routing: {
|
routing: {
|
||||||
fallbackType: 'rewrite',
|
fallbackType: 'rewrite',
|
||||||
prefixDefaultLocale: false,
|
prefixDefaultLocale: false,
|
||||||
|
|
27
cspell.json
27
cspell.json
|
@ -6,33 +6,24 @@
|
||||||
"adam",
|
"adam",
|
||||||
"animejs",
|
"animejs",
|
||||||
"AMOLED",
|
"AMOLED",
|
||||||
"Apóyanos",
|
|
||||||
"Astronav",
|
"Astronav",
|
||||||
|
"Briel",
|
||||||
"brhm",
|
"brhm",
|
||||||
"Brhm",
|
"Brhm",
|
||||||
"Briel",
|
|
||||||
"bryan",
|
"bryan",
|
||||||
"canoa",
|
|
||||||
"Canoa",
|
"Canoa",
|
||||||
|
"canoa",
|
||||||
"cfasync",
|
"cfasync",
|
||||||
"contáctanos",
|
|
||||||
"Contáctanos",
|
|
||||||
"createdAsc",
|
"createdAsc",
|
||||||
"createdDefault",
|
"createdDefault",
|
||||||
"createdDesc",
|
"createdDesc",
|
||||||
"daniel",
|
"daniel",
|
||||||
"donándonos",
|
"FMPEG",
|
||||||
"encriptación",
|
|
||||||
"Español",
|
|
||||||
"ferrocyante",
|
"ferrocyante",
|
||||||
"flatpaks",
|
"flatpaks",
|
||||||
"FMPEG",
|
|
||||||
"Galdámez",
|
"Galdámez",
|
||||||
"García",
|
"García",
|
||||||
"Garro",
|
"Garro",
|
||||||
"geolocalización",
|
|
||||||
"infórmalo",
|
|
||||||
"infórmanos",
|
|
||||||
"isnan",
|
"isnan",
|
||||||
"itro",
|
"itro",
|
||||||
"jace",
|
"jace",
|
||||||
|
@ -43,42 +34,38 @@
|
||||||
"Jokagi",
|
"Jokagi",
|
||||||
"junicode",
|
"junicode",
|
||||||
"Junicode",
|
"Junicode",
|
||||||
"Kristijan",
|
|
||||||
"kristijanribaric",
|
"kristijanribaric",
|
||||||
|
"Kristijan",
|
||||||
"laggy",
|
"laggy",
|
||||||
"larvey",
|
"larvey",
|
||||||
"Larvey",
|
"Larvey",
|
||||||
"linaarchsum",
|
|
||||||
"linuxarmsum",
|
"linuxarmsum",
|
||||||
"linuxsum",
|
"linuxsum",
|
||||||
|
"linaarchsum",
|
||||||
"mfsa",
|
"mfsa",
|
||||||
"mozilla",
|
"mozilla",
|
||||||
"Nehalem",
|
"Nehalem",
|
||||||
"notarización",
|
|
||||||
"NSIS",
|
"NSIS",
|
||||||
"OCSP",
|
"OCSP",
|
||||||
"oscar",
|
"oscar",
|
||||||
"Otero",
|
"Otero",
|
||||||
"patreon",
|
"patreon",
|
||||||
"Pdzly",
|
"Pdzly",
|
||||||
"Refactorización",
|
|
||||||
"Ribaric",
|
"Ribaric",
|
||||||
"taroj",
|
"taroj",
|
||||||
"testid",
|
"testid",
|
||||||
"theming",
|
"theming",
|
||||||
"tsconfigs",
|
|
||||||
"tuta",
|
"tuta",
|
||||||
|
"tsconfigs",
|
||||||
"unfloatable",
|
"unfloatable",
|
||||||
"unfocusing",
|
"unfocusing",
|
||||||
"unrs",
|
|
||||||
"updatedAsc",
|
"updatedAsc",
|
||||||
"updatedDefault",
|
"updatedDefault",
|
||||||
"updatedDesc",
|
"updatedDesc",
|
||||||
"VAAPI",
|
"VAAPI",
|
||||||
"wmfcdm",
|
"wmfcdm",
|
||||||
"workerd",
|
|
||||||
"xmark",
|
|
||||||
"XPCOM",
|
"XPCOM",
|
||||||
|
"xmark",
|
||||||
"zsync"
|
"zsync"
|
||||||
],
|
],
|
||||||
"flagWords": [],
|
"flagWords": [],
|
||||||
|
|
|
@ -37,7 +37,6 @@
|
||||||
"@fortawesome/free-solid-svg-icons": "6.7.1",
|
"@fortawesome/free-solid-svg-icons": "6.7.1",
|
||||||
"@types/react": "^19.1.6",
|
"@types/react": "^19.1.6",
|
||||||
"@types/react-dom": "^19.1.5",
|
"@types/react-dom": "^19.1.5",
|
||||||
"arktype": "^2.1.20",
|
|
||||||
"animejs": "^4.0.2",
|
"animejs": "^4.0.2",
|
||||||
"astro": "5.7.10",
|
"astro": "5.7.10",
|
||||||
"astro-navbar": "2.3.7",
|
"astro-navbar": "2.3.7",
|
||||||
|
|
712
pnpm-lock.yaml
generated
712
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,2 @@
|
||||||
onlyBuiltDependencies:
|
onlyBuiltDependencies:
|
||||||
- esbuild
|
|
||||||
- lefthook
|
- lefthook
|
||||||
- sharp
|
|
||||||
- unrs-resolver
|
|
||||||
- workerd
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -3,8 +3,7 @@ import { icon, library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faSort, faSortDown, faSortUp } from '@fortawesome/free-solid-svg-icons'
|
import { faSort, faSortDown, faSortUp } from '@fortawesome/free-solid-svg-icons'
|
||||||
import Xmark from '~/icons/XmarkIcon.astro'
|
import Xmark from '~/icons/XmarkIcon.astro'
|
||||||
import { type ZenTheme } from '~/mods'
|
import { type ZenTheme } from '~/mods'
|
||||||
import { type Locale } from '~/types/i18n'
|
import { getPath, type Locale } from '~/utils/i18n'
|
||||||
import { getPath } from '~/utils/i18n'
|
|
||||||
|
|
||||||
// Add icons to the library
|
// Add icons to the library
|
||||||
library.add(faSort, faSortUp, faSortDown)
|
library.add(faSort, faSortUp, faSortDown)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { type Locale } from '~/types/i18n'
|
|
||||||
import { getUI } from '~/utils/i18n'
|
import { getUI } from '~/utils/i18n'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,7 +5,7 @@ import { getUI } from '~/utils/i18n'
|
||||||
* @param locale The locale to use for labels
|
* @param locale The locale to use for labels
|
||||||
* @param checksums Record<string, string> mapping filenames to SHA-256 hashes
|
* @param checksums Record<string, string> mapping filenames to SHA-256 hashes
|
||||||
*/
|
*/
|
||||||
export function getReleasesWithChecksums(locale: Locale) {
|
export function getReleasesWithChecksums(locale: string) {
|
||||||
const {
|
const {
|
||||||
routes: {
|
routes: {
|
||||||
download: {
|
download: {
|
||||||
|
|
|
@ -1,47 +1,19 @@
|
||||||
import { type } from 'arktype'
|
const UI_EN = (await import('~/i18n/en/translation.json', { with: { type: 'json' } })).default
|
||||||
|
const UI_JA = (await import('~/i18n/ja/translation.json', { with: { type: 'json' } })).default
|
||||||
|
|
||||||
import languages from '~/i18n/locales.json' with { type: 'json' }
|
|
||||||
import { i18nSchema } from '~/schemas/i18n'
|
|
||||||
import { type I18nType, type Locale } from '~/types/i18n'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of all supported locales.
|
|
||||||
*/
|
|
||||||
export const locales = Object.keys(languages) as Locale[]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps locale keys to their corresponding translation objects.
|
|
||||||
*/
|
|
||||||
export const translations: Record<Locale, I18nType> = Object.fromEntries(
|
|
||||||
Object.entries(import.meta.glob('~/i18n/**/translation.json', { eager: true })).map(
|
|
||||||
([key, value]) => {
|
|
||||||
const result = i18nSchema.I18n(value)
|
|
||||||
|
|
||||||
if (result instanceof type.errors) {
|
|
||||||
throw new Error(`Invalid translation file (${key}):\n${' '.repeat(2)}${result.summary}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return [/i18n\/([A-z]{2})\/translation.json/.exec(key)?.[1], result]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constants for i18n configuration.
|
|
||||||
*/
|
|
||||||
export const i18n = {
|
export const i18n = {
|
||||||
DEFAULT_LOCALE: 'en',
|
DEFAULT_LOCALE: 'en',
|
||||||
LOCALES: Object.entries(languages).map(([key, locale]) => {
|
LOCALES: [
|
||||||
return {
|
{ label: 'English', value: 'en', ui: UI_EN, intl: 'en-US' },
|
||||||
...locale,
|
{ label: '日本語', value: 'ja', ui: UI_JA, intl: 'ja-JP' },
|
||||||
ui: translations[key as Locale],
|
],
|
||||||
}
|
}
|
||||||
}),
|
|
||||||
} as const
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the intl locale string for a given locale.
|
* Type definition for UI translations based on the English translation
|
||||||
*/
|
*/
|
||||||
export const getIntlLocale = (locale: Locale) => {
|
export type UIProps = typeof UI_EN | typeof UI_JA
|
||||||
return languages[locale]?.intl
|
|
||||||
|
export const getIntlLocale = (locale: string) => {
|
||||||
|
return i18n.LOCALES.find(l => l.value === locale)?.intl
|
||||||
}
|
}
|
||||||
|
|
|
@ -285,7 +285,7 @@
|
||||||
},
|
},
|
||||||
"securityNotice": {
|
"securityNotice": {
|
||||||
"title": "Verified & Secure Downloads",
|
"title": "Verified & Secure Downloads",
|
||||||
"description": "All Zen downloads are signed and verified for your security. We recommend downloading directly from our official website or GitHub repository. If your download seems broken or gets flagged by your antivirus, please <a href='https://github.com/zen-browser/desktop/issues/new/choose' class='zen-link'>report it to us</a>."
|
"description": "All Zen downloads are signed and verified for your security. We recommend downloading directly from our official website or GitHub repository. If your download seems broken or gets flagged by your antivirus, please <a href='https://github.com/zen-browser/desktop/issues/new/choose' class='zen-link ml-1'>report it to us</a>."
|
||||||
},
|
},
|
||||||
"platformNames": {
|
"platformNames": {
|
||||||
"mac": "macOS",
|
"mac": "macOS",
|
||||||
|
|
|
@ -1,511 +0,0 @@
|
||||||
{
|
|
||||||
"routes": {
|
|
||||||
"index": {
|
|
||||||
"title": "Zen Browser",
|
|
||||||
"hero": {
|
|
||||||
"title": [
|
|
||||||
{ "text": "bienvenido ", "highlight": false },
|
|
||||||
{ "text": "a ", "highlight": false },
|
|
||||||
{ "text": "\n", "highlight": false },
|
|
||||||
{ "text": "un ", "highlight": false },
|
|
||||||
{ "text": "internet más ", "highlight": false },
|
|
||||||
{ "text": "tranquilo", "highlight": true }
|
|
||||||
],
|
|
||||||
"description": [
|
|
||||||
"Hermosamente diseñado, enfocado en la privacidad y repleto de funcionalidades.",
|
|
||||||
"Nos importa tu experiencia, no tus datos."
|
|
||||||
],
|
|
||||||
"buttons": {
|
|
||||||
"beta": "¡Beta ya disponible!",
|
|
||||||
"support": "Apóyanos ❤️"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"features": {
|
|
||||||
"titles": ["Máxima ", "productividad", ""],
|
|
||||||
"description": "Zen está repleto de funcionalidades que te ayudan a mantenerte productivo y enfocado. Los navegadores deberían ser herramientas que te ayuden a hacer las cosas, no distracciones que te alejen de tu trabajo.",
|
|
||||||
"featureTabs": {
|
|
||||||
"workspaces": {
|
|
||||||
"title": "Espacios de trabajo",
|
|
||||||
"description": "Organiza tus pestañas en Espacios de trabajo para mantener tus proyectos separados y organizados, y cambia entre ellos con facilidad."
|
|
||||||
},
|
|
||||||
"compactMode": {
|
|
||||||
"title": "Modo compacto",
|
|
||||||
"description": "El Modo compacto de Zen te brinda más espacio en pantalla al ocultar la barra de pestañas cuando no la necesitas, y mostrándola cuando sí."
|
|
||||||
},
|
|
||||||
"glance": {
|
|
||||||
"title": "Vistazo",
|
|
||||||
"description": "Vistazo te permite cambiar rápidamente entre tus pestañas más utilizadas, sin tener que desplazarte por tu historial."
|
|
||||||
},
|
|
||||||
"splitView": {
|
|
||||||
"title": "Vista dividida",
|
|
||||||
"description": "La Vista dividida te permite ver dos pestañas una al lado de la otra, lo que facilita la comparación y el cambio entre ellas."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sponsors": {
|
|
||||||
"title": "Nuestros patrocinadores",
|
|
||||||
"description": "Estamos agradecidos con nuestros patrocinadores por su apoyo. Ellos nos ayudan a mantener el proyecto vivo.<br />¡Tú también puedes ser parte de este viaje <a href=\"/donate\" class=\"zen-link\">donándonos directamente</a>!",
|
|
||||||
"sponsors": {
|
|
||||||
"tuta": {
|
|
||||||
"name": "Tuta",
|
|
||||||
"url": "https://tuta.com/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"community": {
|
|
||||||
"title": ["Nuestros ", "Valores ", "Fundamentales"],
|
|
||||||
"description": "Hacemos de esto no solo una prioridad, sino una necesidad para garantizar que Zen siempre encuentre el equilibrio adecuado entre belleza, rendimiento y privacidad. Estamos comprometidos a hacer de Zen el navegador más hermoso, productivo y respetuoso con la privacidad, sin comprometer tu experiencia.",
|
|
||||||
"lists": {
|
|
||||||
"freeAndOpenSource": {
|
|
||||||
"title": "Libre y de código abierto",
|
|
||||||
"description": "Zen es un software libre y de código abierto, lo que significa que puedes usarlo sin ningún costo y puedes modificarlo para adaptarlo a tus necesidades."
|
|
||||||
},
|
|
||||||
"simpleYetPowerful": {
|
|
||||||
"title": "Simple pero poderoso",
|
|
||||||
"description": "Zen es simple de usar, pero lo suficientemente poderoso como para manejar tus tareas diarias."
|
|
||||||
},
|
|
||||||
"privateAndAlwaysUpToDate": {
|
|
||||||
"title": "Privado y siempre actualizado",
|
|
||||||
"description": "Zen es privado y siempre está actualizado, lo que significa que puedes usarlo sin ningún costo y puedes modificarlo para adaptarlo a tus necesidades."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"images": {
|
|
||||||
"community": {
|
|
||||||
"alt": "Comunidad"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mods": {
|
|
||||||
"title": "Zen Mods",
|
|
||||||
"description": "Explora nuestra diversa colección de Zen Mods, complementos y temas creados por la comunidad para Zen Browser. Descubre un tema que se adapte a tu estado de ánimo y un complemento que satisfaga cada requisito. ¡Comienza a personalizar tu experiencia de navegación hoy mismo!",
|
|
||||||
"pagination": {
|
|
||||||
"pagination": "{input} de {totalPages} ({totalItems} elementos)"
|
|
||||||
},
|
|
||||||
"search": "Escribe para buscar...",
|
|
||||||
"by": "por",
|
|
||||||
"sort": {
|
|
||||||
"lastCreated": "Última creación",
|
|
||||||
"lastUpdated": "Última actualización",
|
|
||||||
"perPage": "Por página"
|
|
||||||
},
|
|
||||||
"noResults": "No se encontraron resultados",
|
|
||||||
"noResultsDescription": "Intenta buscar un término diferente o vuelve más tarde.",
|
|
||||||
"slug": {
|
|
||||||
"title": "{name} - Zen Mods",
|
|
||||||
"description": "Aprende más sobre el mod {name} disponible en Zen",
|
|
||||||
"alert": {
|
|
||||||
"description": "Necesitas tener Zen instalado para instalar este tema.",
|
|
||||||
"button": "¡Descargar ahora!"
|
|
||||||
},
|
|
||||||
"createdBy": "Creado por {author} • <b>v{version}</b>",
|
|
||||||
"creationDate": "Fecha de creación: <b>{createdAt}</b>",
|
|
||||||
"latestUpdate": "Última actualización: <b>{updatedAt}</b>",
|
|
||||||
"visitModHomepage": "Visitar la página del mod",
|
|
||||||
"installMod": "Instalar mod 🎉",
|
|
||||||
"uninstallMod": "Desinstalar mod",
|
|
||||||
"back": "Atrás"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"releaseNotes": {
|
|
||||||
"topSection": {
|
|
||||||
"title": "Changelog",
|
|
||||||
"description": "Mantente al día con los últimos cambios en Zen. Desde la <a class=\"zen-link\" href=\"#1.0.0-a.1\">primera versión</a> hasta <a class=\"zen-link\" href=\"#{latestVersion}\">{latestVersion}</a>, hemos estado trabajando arduamente para hacer de Zen lo mejor posible. ¡Gracias a todos por sus comentarios! ❤️"
|
|
||||||
},
|
|
||||||
"list": {
|
|
||||||
"support": "¡Danos un poco de apoyo!",
|
|
||||||
"navigateToVersion": "Navegar a la versión..."
|
|
||||||
},
|
|
||||||
"itemType": {
|
|
||||||
"fix": "Corregido",
|
|
||||||
"feature": "Añadido",
|
|
||||||
"known": "Conocido",
|
|
||||||
"break": "Ruptura",
|
|
||||||
"theme": "Tema",
|
|
||||||
"security": "Seguridad",
|
|
||||||
"change": "Cambio"
|
|
||||||
},
|
|
||||||
"backToTop": "Volver al principio",
|
|
||||||
"chooseVersion": "Elegir versión",
|
|
||||||
"components": {
|
|
||||||
"releaseNoteItem": {
|
|
||||||
"twilight": "Twilight",
|
|
||||||
"twilightChanges": "Cambios de Twilight",
|
|
||||||
"releaseChanges": "v{version}",
|
|
||||||
"firefoxVersion": "Firefox {version}",
|
|
||||||
"githubRelease": "GitHub Release",
|
|
||||||
"workflowRun": "Workflow run",
|
|
||||||
"compareChanges": "Comparar cambios",
|
|
||||||
"twilightWarning": "Ten en cuenta que Twilight es una versión preliminar de Zen. Puede contener errores y funcionalidades incompletas.",
|
|
||||||
"reportIssues": " Si encuentras algún problema, infórmalo en <a rel=\"noopener noreferrer\" target=\"_blank\" href=\"https://github.com/zen-browser/desktop/issues/\" class=\"zen-link\">la página de problemas</a>.",
|
|
||||||
"learnMore": "Aprender más",
|
|
||||||
"viewIssue": "Ver el problema número {issue} en GitHub"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"slug": {
|
|
||||||
"title": "Notas de la versión",
|
|
||||||
"redirect": "Redirigiendo a las notas de la versión {version}..."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"about": {
|
|
||||||
"title": "Acerca de Zen",
|
|
||||||
"description": "Simplemente somos un grupo de desarrolladores y diseñadores que se preocupan por tu experiencia en la web. Creemos que Internet debería ser un lugar donde puedas explorar, aprender y conectarte sin preocuparte por la recopilación de tus datos.",
|
|
||||||
"littleHelp": "¿Un poco de ayuda?",
|
|
||||||
"mainTeam": {
|
|
||||||
"title": "Equipo Principal",
|
|
||||||
"description": "Esta lista muestra a los miembros del equipo principal que están trabajando arduamente para brindarte la mejor experiencia de navegación.",
|
|
||||||
"subTitle": {
|
|
||||||
"browser": "Navegador",
|
|
||||||
"website": "Sitio web y marca"
|
|
||||||
},
|
|
||||||
"members": {
|
|
||||||
"browser": {
|
|
||||||
"mauro": {
|
|
||||||
"name": "Mauro B.",
|
|
||||||
"description": "Creador, Desarrollador Principal",
|
|
||||||
"link": "https://cheff.dev/"
|
|
||||||
},
|
|
||||||
"jan": {
|
|
||||||
"name": "Jan Heres",
|
|
||||||
"description": "Contribuyente activo y ayuda con las compilaciones de MacOS",
|
|
||||||
"link": "https://janheres.eu/"
|
|
||||||
},
|
|
||||||
"bryan": {
|
|
||||||
"name": "Bryan Galdámez",
|
|
||||||
"description": "Gran contribuyente en las funcionalidades de temas",
|
|
||||||
"link": "https://josuegalre.netlify.app/"
|
|
||||||
},
|
|
||||||
"oscar": {
|
|
||||||
"name": "Oscar Gonzalez",
|
|
||||||
"description": "Ingeniero de Confiabilidad del Sitio (SRE) y firma de código.",
|
|
||||||
"link": false
|
|
||||||
},
|
|
||||||
"daniel": {
|
|
||||||
"name": "Daniel García",
|
|
||||||
"description": "Mantenedor de certificados de MacOS y notarización de aplicaciones",
|
|
||||||
"link": false
|
|
||||||
},
|
|
||||||
"brhm": {
|
|
||||||
"name": "BrhmDev",
|
|
||||||
"description": "Contribuyente activo con grandes contribuciones",
|
|
||||||
"link": "https://github.com/BrhmDev"
|
|
||||||
},
|
|
||||||
"kristijanribaric": {
|
|
||||||
"name": "Kristijan Ribaric",
|
|
||||||
"description": "Contribuyente activo con vistas divididas / espacios de trabajo",
|
|
||||||
"link": "https://github.com/kristijanribaric"
|
|
||||||
},
|
|
||||||
"larvey": {
|
|
||||||
"name": "Larvey",
|
|
||||||
"description": "Mantenedor de AUR",
|
|
||||||
"link": "https://github.com/LarveyOfficial/"
|
|
||||||
},
|
|
||||||
"studio": {
|
|
||||||
"name": "Studio Movie Girl",
|
|
||||||
"description": "Gran contribuyente con el generador de degradados",
|
|
||||||
"link": "https://github.com/neurokitti"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"website": {
|
|
||||||
"taroj1205": {
|
|
||||||
"name": "Shintaro Jokagi",
|
|
||||||
"description": "Arquitecto Principal del Sitio Web, Liderando la Refactorización y Mejoras Técnicas",
|
|
||||||
"link": "https://github.com/taroj1205"
|
|
||||||
},
|
|
||||||
"jace": {
|
|
||||||
"name": "Jace",
|
|
||||||
"description": "Contribuye al diseño y la marca del sitio web",
|
|
||||||
"link": "https://x.com/JaceThings"
|
|
||||||
},
|
|
||||||
"canoa": {
|
|
||||||
"name": "Canoa",
|
|
||||||
"description": "Contribuyente activo y muy activo en la gestión de problemas y el sitio web",
|
|
||||||
"link": "https://thatcanoa.org/"
|
|
||||||
},
|
|
||||||
"adam": {
|
|
||||||
"name": "Adam",
|
|
||||||
"description": "Marca y diseño",
|
|
||||||
"link": "https://cybrneon.xyz/"
|
|
||||||
},
|
|
||||||
"n7itro": {
|
|
||||||
"name": "n7itro",
|
|
||||||
"description": "Contribuyente activo y escritor de notas de lanzamiento",
|
|
||||||
"link": "https://github.com/n7itro"
|
|
||||||
},
|
|
||||||
"jafeth": {
|
|
||||||
"name": "Jafeth Garro",
|
|
||||||
"description": "Escritor de documentación",
|
|
||||||
"link": "https://iamjafeth.com/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"contributors": {
|
|
||||||
"title": "Contribuyentes",
|
|
||||||
"description": "Esta lista muestra a los contribuyentes que nos han ayudado a hacer de Zen lo mejor que puede ser.",
|
|
||||||
"browser": "Navegador",
|
|
||||||
"website": "Sitio web"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"donate": {
|
|
||||||
"title": "Donar",
|
|
||||||
"description": "Somos un pequeño equipo de desarrolladores que trabaja arduamente para brindarte la mejor experiencia de navegación. Si te gusta lo que hacemos, considera apoyarnos.",
|
|
||||||
"patreon": {
|
|
||||||
"title": "Patreon",
|
|
||||||
"description": "Patreon te permite apoyarnos con una donación mensual. Puedes elegir el nivel de apoyo que mejor se adapte a ti.",
|
|
||||||
"button": "Ir a Patreon"
|
|
||||||
},
|
|
||||||
"koFi": {
|
|
||||||
"title": "Ko-fi",
|
|
||||||
"description": "Ko-fi te permite apoyarnos con una donación única. Puedes elegir la cantidad que mejor se adapte a ti. También están disponibles las donaciones mensuales.",
|
|
||||||
"button": "Ir a Ko-fi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"download": {
|
|
||||||
"title": "Descargar Zen",
|
|
||||||
"description": "Descarga Zen para tu plataforma y experimenta una experiencia de navegación por Internet más consciente. Todas las descargas incluyen sumas de verificación SHA256 para verificación.",
|
|
||||||
"twilightInfo": "Actualmente estás en modo Twilight, lo que significa que estás descargando las últimas funcionalidades y actualizaciones experimentales.",
|
|
||||||
"alertInfo": {
|
|
||||||
"description": "<strong class='font-medium text-zen-blue'>Modo Twilight:</strong> Actualmente estás en modo Twilight, lo que significa que estás descargando las últimas funcionalidades y actualizaciones experimentales."
|
|
||||||
},
|
|
||||||
"platformSelector": {
|
|
||||||
"title": "Selector de Plataforma",
|
|
||||||
"description": "Selecciona tu plataforma para descargar Zen."
|
|
||||||
},
|
|
||||||
"additionalResources": {
|
|
||||||
"title": "Recursos Adicionales",
|
|
||||||
"sourceCode": {
|
|
||||||
"title": "Código Fuente",
|
|
||||||
"description": "Explora el código fuente de Zen en GitHub. Contribuye al proyecto o construye tu propia versión."
|
|
||||||
},
|
|
||||||
"documentation": {
|
|
||||||
"title": "Documentación",
|
|
||||||
"description": "Accede a documentación completa, guías y tutoriales para Zen."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"securityNotice": {
|
|
||||||
"title": "Descargas Verificadas y Seguras",
|
|
||||||
"description": "Todas las descargas de Zen están firmadas y verificadas para tu seguridad. Recomendamos descargar directamente desde nuestro sitio web oficial o repositorio de GitHub. Si tu descarga parece estar rota o es marcada por tu antivirus, por favor <a href='https://github.com/zen-browser/desktop/issues/new/choose' class='zen-link'>infórmanos</a>."
|
|
||||||
},
|
|
||||||
"platformNames": {
|
|
||||||
"mac": "MacOS",
|
|
||||||
"windows": "Windows",
|
|
||||||
"linux": "Linux",
|
|
||||||
"macDownload": "Descarga de MacOS",
|
|
||||||
"windowsDownload": "Descarga de Windows",
|
|
||||||
"linuxDownload": "Descarga de Linux"
|
|
||||||
},
|
|
||||||
"platformDescriptions": {
|
|
||||||
"mac": "Funciona en nuevos Macs de Apple (M-Series) y en Macs Intel más antiguos.<br />Requiere macOS 11.0 o posterior.",
|
|
||||||
"windows": "Funciona en Windows 10 y Windows 11.<br />¿No estás seguro de qué versión obtener? La mayoría de las personas deberían elegir el instalador de 64 bits.",
|
|
||||||
"linux": "Funciona con muchas versiones de Linux.<br />Elige la descarga que coincida con tu sistema."
|
|
||||||
},
|
|
||||||
"links": {
|
|
||||||
"macos": { "universal": "Universal" },
|
|
||||||
"windows": { "64bit": "64-bit (Recomendado)", "ARM64": "ARM64" },
|
|
||||||
"linux": {
|
|
||||||
"flathub": "Flathub",
|
|
||||||
"x86_64": "Tarball",
|
|
||||||
"aarch64": "Tarball"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"buttonCard": {
|
|
||||||
"copy": "Copiar",
|
|
||||||
"showChecksum": "Mostrar SHA-256",
|
|
||||||
"beta": "Beta"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"privacyPolicy": {
|
|
||||||
"title": "Política de Privacidad",
|
|
||||||
"lastUpdated": "Última actualización: 2025-02-5",
|
|
||||||
"sections": {
|
|
||||||
"introduction": {
|
|
||||||
"title": "Introducción",
|
|
||||||
"body": "¡Bienvenido a Zen! Tu privacidad es nuestra prioridad. Esta Política de Privacidad describe los tipos de información personal que recopilamos, cómo la usamos y las medidas que tomamos para proteger tus datos cuando usas Zen.",
|
|
||||||
"summary": "No vendemos datos - No recopilamos datos - No te rastreamos"
|
|
||||||
},
|
|
||||||
"noCollect": {
|
|
||||||
"title": "1. Información que No Recopilamos",
|
|
||||||
"body": "Zen está diseñado con la privacidad en mente. No recopilamos, almacenamos ni compartimos ninguno de tus datos personales. Esto es lo que significa:"
|
|
||||||
},
|
|
||||||
"noTelemetry": {
|
|
||||||
"title": "1.1. No Telemetría",
|
|
||||||
"body": "No recopilamos ningún dato de telemetría ni informes de fallos.",
|
|
||||||
"body2": "Zen ha eliminado la telemetría integrada en Mozilla Firefox. Hemos eliminado toda la recopilación de datos de telemetría y los informes de fallos."
|
|
||||||
},
|
|
||||||
"noPersonalData": {
|
|
||||||
"title": "1.2. No Recopilación de Datos Personales",
|
|
||||||
"body": "Zen no recopila ninguna información personal, como tu dirección IP, historial de navegación, consultas de búsqueda o datos de formularios."
|
|
||||||
},
|
|
||||||
"noThirdParty": {
|
|
||||||
"title": "1.3. No Seguimiento de Terceros",
|
|
||||||
"body": "No permitimos que rastreadores de terceros o herramientas de análisis operen dentro de Zen. Tu actividad de navegación permanece completamente privada y no se comparte con ningún tercero. Mozilla no se considera un tercero, ya que es la base de Zen."
|
|
||||||
},
|
|
||||||
"externalConnections": {
|
|
||||||
"title": "1.4. Conexiones Externas Realizadas al Inicio",
|
|
||||||
"body": "Zen puede realizar conexiones externas al inicio para buscar actualizaciones y garantizar que el navegador esté actualizado en complementos, extensiones, verificar la conectividad y los servicios de geolocalización/notificaciones push para cumplir con los estándares web. Nosotros, en Zen, no recopilamos ningún dato de estas conexiones, pero pueden ser registradas por servicios de terceros o sitios web que visites. Estas conexiones son necesarias para el correcto funcionamiento del navegador y no se utilizan con fines de seguimiento o perfilado. Pueden deshabilitarse a través de las banderas del navegador (about:config)."
|
|
||||||
},
|
|
||||||
"localStorage": {
|
|
||||||
"title": "2. Información Almacenada Localmente en Tu Dispositivo"
|
|
||||||
},
|
|
||||||
"browsingData": {
|
|
||||||
"title": "2.1. Datos de Navegación",
|
|
||||||
"body": "Zen almacena ciertos datos localmente en tu dispositivo para mejorar tu experiencia de navegación. Esto incluye:"
|
|
||||||
},
|
|
||||||
"cookies": {
|
|
||||||
"title": "Cookies",
|
|
||||||
"body": "Las cookies se almacenan localmente en tu dispositivo y no se comparten con Zen ni con ningún tercero. Tienes control total sobre la gestión de cookies a través de la configuración del navegador."
|
|
||||||
},
|
|
||||||
"cache": {
|
|
||||||
"title": "Caché y Archivos Temporales",
|
|
||||||
"body": "Zen puede almacenar archivos de caché y otros datos temporales localmente para mejorar el rendimiento. Estos archivos se pueden eliminar en cualquier momento a través de la configuración del navegador."
|
|
||||||
},
|
|
||||||
"settings": {
|
|
||||||
"title": "2.2. Configuración y Preferencias",
|
|
||||||
"body": "Cualquier personalización, configuración y preferencia que realices dentro de Zen se almacena localmente en tu dispositivo. No tenemos acceso ni control sobre estos datos."
|
|
||||||
},
|
|
||||||
"sync": {
|
|
||||||
"title": "3. Función de Sincronización",
|
|
||||||
"body": "Zen ofrece una función de \"Sincronización\", que se implementa utilizando la función de sincronización de Mozilla Firefox. Esta función te permite sincronizar tus marcadores, historial, contraseñas y otros datos en múltiples dispositivos. Para que esta función funcione, tus datos se cifran y se almacenan en los servidores de Mozilla y se tratan de acuerdo con su Política de Privacidad. Nosotros, en Zen, no podemos ver ninguno de estos datos.",
|
|
||||||
"link1": "Sincronización de Mozilla Firefox",
|
|
||||||
"link2": "Así es como almacenamos tus contraseñas"
|
|
||||||
},
|
|
||||||
"addons": {
|
|
||||||
"title": "4. Complementos y \"Mods\"",
|
|
||||||
"body": "Puedes instalar complementos desde addons.mozilla.org. Zen verifica periódicamente si hay actualizaciones para estos complementos.\nTambién puedes instalar \"Mods\" desde zen-browser.app/mods. Estos Mods son alojados por nuestros servicios y siguen la misma política de privacidad que nuestro sitio web. No recopilamos ningún dato de estos Mods, son contenido puramente estático que se descarga en tu dispositivo."
|
|
||||||
},
|
|
||||||
"security": {
|
|
||||||
"title": "5. Seguridad de los Datos",
|
|
||||||
"body": "Aunque Zen no recopila tus datos, estamos comprometidos a proteger la información que se almacena localmente en tu dispositivo y, si utilizas la función de Sincronización, los datos cifrados almacenados en los servidores de Mozilla. Te recomendamos que utilices contraseñas seguras, habilites la encriptación del dispositivo y actualices regularmente tu software para garantizar que tus datos permanezcan seguros.",
|
|
||||||
"note": "Ten en cuenta que la mayoría de las medidas de seguridad son gestionadas por Mozilla Firefox."
|
|
||||||
},
|
|
||||||
"control": {
|
|
||||||
"title": "6. Tu Control",
|
|
||||||
"deletionTitle": "6.1. Eliminación de Datos",
|
|
||||||
"deletionBody": "Tienes control total sobre todos los datos almacenados localmente en tu dispositivo por Zen. Puedes borrar tus datos de navegación, cookies y caché en cualquier momento utilizando la configuración del navegador."
|
|
||||||
},
|
|
||||||
"website": {
|
|
||||||
"title": "7. Nuestro Sitio Web y Servicios",
|
|
||||||
"body": "El sitio web y los servicios de Zen no utilizan ningún servicio de análisis, seguimiento o CDN de terceros. No recopilamos ninguna información personal de los usuarios que visitan nuestro sitio web. El sitio web está alojado en Cloudflare, pero con análisis y seguimiento deshabilitados, Cloudflare puede recopilar algunos datos analíticos de las solicitudes HTTP para proporcionar mejoras de seguridad y rendimiento. Sin embargo, estos datos no están vinculados a ninguna información personal y no se utilizan con fines de seguimiento.",
|
|
||||||
"externalLinksTitle": "7.1. Enlaces Externos",
|
|
||||||
"externalLinksBody": "Zen puede contener enlaces a sitios web o servicios externos que no son propiedad ni están operados por nosotros. No somos responsables del contenido o las prácticas de privacidad de estos sitios. Te recomendamos que revises las políticas de privacidad de estos sitios antes de proporcionarles cualquier información personal."
|
|
||||||
},
|
|
||||||
"changes": {
|
|
||||||
"title": "8. Cambios en Esta Política de Privacidad",
|
|
||||||
"body": "Podemos actualizar esta Política de Privacidad de vez en cuando para reflejar cambios en nuestras prácticas o requisitos legales. Te notificaremos sobre cualquier cambio significativo actualizando la fecha de vigencia en la parte superior de esta política. El uso continuado de Zen después de dichos cambios constituye tu aceptación de los nuevos términos."
|
|
||||||
},
|
|
||||||
"otherTelemetry": {
|
|
||||||
"title": "9. Otra telemetría realizada por Mozilla Firefox",
|
|
||||||
"body": "Intentamos deshabilitar toda la recopilación de datos de telemetría en Zen. Pero, es posible que hayamos pasado por alto algunos. Consulta los siguientes enlaces para obtener más información.",
|
|
||||||
"firefoxPrivacyNotice": "Aviso de Privacidad de Firefox",
|
|
||||||
"forMoreInformation": "para más información."
|
|
||||||
},
|
|
||||||
"contact": {
|
|
||||||
"title": "10. Contáctanos",
|
|
||||||
"body": "Si tienes alguna pregunta o inquietud sobre esta Política de Privacidad o Zen, por favor contáctanos en:",
|
|
||||||
"discord": "Discord: ",
|
|
||||||
"discordLink": "Discord de Zen",
|
|
||||||
"github": "GitHub: ",
|
|
||||||
"githubLink": "Organización"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"welcome": {
|
|
||||||
"title": ["Bienvenido ", "a ", "Zen!"]
|
|
||||||
},
|
|
||||||
"whatsNew": {
|
|
||||||
"title": "¡Novedades en {latestVersion.version}!",
|
|
||||||
"reportIssue": "Reportar un problema",
|
|
||||||
"joinDiscord": "Unirse a nuestro Discord",
|
|
||||||
"readFullReleaseNotes": "Leer las notas de la versión completas"
|
|
||||||
},
|
|
||||||
"notFound": {
|
|
||||||
"title": "Página No Encontrada",
|
|
||||||
"description": "Lo sentimos, la página que estás buscando no existe o ha sido movida.",
|
|
||||||
"button": "Ir a Inicio"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"layout": {
|
|
||||||
"index": {
|
|
||||||
"title": "Zen Browser",
|
|
||||||
"description": "Diseñado con belleza, enfocado en la privacidad y repleto de características."
|
|
||||||
},
|
|
||||||
"mods": {
|
|
||||||
"title": "Zen Mods",
|
|
||||||
"description": "Explora nuestra diversa colección de Zen Mods, complementos y temas creados por la comunidad para Zen. Descubre un tema que se adapte a cada estado de ánimo y un complemento que satisfaga cada necesidad. ¡Comienza a personalizar tu experiencia de navegación hoy mismo!"
|
|
||||||
},
|
|
||||||
"releaseNotes": {
|
|
||||||
"title": "Notas de la versión - Zen",
|
|
||||||
"description": "¡Mantente al día con los últimos cambios en Zen! Desde el primer lanzamiento hasta {latestVersion}, hemos estado trabajando arduamente para hacer de Zen lo mejor que puede ser. ¡Gracias a todos por sus comentarios! ❤️"
|
|
||||||
},
|
|
||||||
"about": {
|
|
||||||
"title": "Acerca de Zen",
|
|
||||||
"description": "Simplemente somos un grupo de desarrolladores y diseñadores que se preocupan por tu experiencia en la web. Creemos que Internet debería ser un lugar donde puedas explorar, aprender y conectarte sin preocuparte por la recopilación de tus datos."
|
|
||||||
},
|
|
||||||
"donate": {
|
|
||||||
"title": "Donar - Zen",
|
|
||||||
"description": "Somos un pequeño equipo de desarrolladores que trabaja arduamente para brindarte la mejor experiencia de navegación. Si te gusta lo que hacemos, considera apoyarnos."
|
|
||||||
},
|
|
||||||
"download": {
|
|
||||||
"title": "Descargar - Zen",
|
|
||||||
"description": "Descarga Zen para tu plataforma y experimenta una navegación por Internet más consciente. Todas las descargas incluyen sumas de verificación SHA256 para verificación."
|
|
||||||
},
|
|
||||||
"privacyPolicy": {
|
|
||||||
"title": "Política de Privacidad - Zen",
|
|
||||||
"description": "Tu privacidad es nuestra prioridad. Esta Política de Privacidad describe los tipos de información personal que recopilamos, cómo la usamos y los pasos que tomamos para proteger tus datos cuando usas Zen."
|
|
||||||
},
|
|
||||||
"welcome": {
|
|
||||||
"title": "¡Bienvenido!",
|
|
||||||
"description": "¡Bienvenido a Zen!"
|
|
||||||
},
|
|
||||||
"whatsNew": {
|
|
||||||
"title": "¡Novedades en {latestVersion.version}!"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"components": {
|
|
||||||
"footer": {
|
|
||||||
"title": "Zen Browser",
|
|
||||||
"description": "Diseñado con belleza, enfocado en la privacidad y repleto de características. Nos importa tu experiencia, no tus datos.",
|
|
||||||
"download": "Descargar",
|
|
||||||
"followUs": "Síguenos",
|
|
||||||
"aboutUs": "Acerca de Nosotros",
|
|
||||||
"teamAndContributors": "Equipo y Contribuyentes",
|
|
||||||
"privacyPolicy": "Política de Privacidad",
|
|
||||||
"getStarted": "Comenzar",
|
|
||||||
"documentation": "Documentación",
|
|
||||||
"zenMods": "Zen Mods",
|
|
||||||
"releaseNotes": "Notas de la Versión",
|
|
||||||
"getHelp": "Obtener Ayuda",
|
|
||||||
"discord": "Discord",
|
|
||||||
"uptimeStatus": "Disponibilidad",
|
|
||||||
"reportAnIssue": "Reportar un Problema",
|
|
||||||
"twilight": "Twilight",
|
|
||||||
"madeWith": "Hecho con <span aria-label='love'>❤️</span> por el <a href='{link}' class='zen-link inline-block font-bold'>Equipo de Zen</a>"
|
|
||||||
},
|
|
||||||
"nav": {
|
|
||||||
"brand": "Zen Browser",
|
|
||||||
"menu": {
|
|
||||||
"gettingStarted": "Comenzar",
|
|
||||||
"usefulLinks": "Enlaces Útiles",
|
|
||||||
"mods": "Mods",
|
|
||||||
"download": "Descargar",
|
|
||||||
"discord": "Discord",
|
|
||||||
"releaseNotes": "Notas de la Versión",
|
|
||||||
"zenMods": "Zen Mods",
|
|
||||||
"tryZenMods": "Probar Zen Mods",
|
|
||||||
"zenModsDesc": "Personaliza tu experiencia de navegación con Zen Mods.",
|
|
||||||
"releaseNotesDesc": "Mantente al día con las últimas características y mejoras.",
|
|
||||||
"discordDesc": "¡Únete a nuestra comunidad en Discord para chatear con otros usuarios de Zen!",
|
|
||||||
"donate": "Donar ❤️",
|
|
||||||
"donateDesc": "Apoya el desarrollo de Zen con una donación.",
|
|
||||||
"aboutUs": "Acerca de Nosotros 🌟",
|
|
||||||
"aboutUsDesc": "Conoce más sobre el equipo detrás de Zen.",
|
|
||||||
"documentation": "Documentación",
|
|
||||||
"documentationDesc": "Aprende a usar Zen con nuestra documentación.",
|
|
||||||
"github": "GitHub",
|
|
||||||
"githubDesc": "Contribuye al desarrollo de Zen en GitHub.",
|
|
||||||
"menu": "Menú"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -285,7 +285,7 @@
|
||||||
},
|
},
|
||||||
"securityNotice": {
|
"securityNotice": {
|
||||||
"title": "検証済み・安全なダウンロード",
|
"title": "検証済み・安全なダウンロード",
|
||||||
"description": "すべてのZenダウンロードは署名・検証済みです。公式サイトまたはGitHubからのダウンロードを推奨します。ダウンロードに問題がある場合やウイルス対策で警告が出た場合は、<a href='https://github.com/zen-browser/desktop/issues/new/choose' class='zen-link'>ご報告ください</a>。"
|
"description": "すべてのZenダウンロードは署名・検証済みです。公式サイトまたはGitHubからのダウンロードを推奨します。ダウンロードに問題がある場合やウイルス対策で警告が出た場合は、<a href='https://github.com/zen-browser/desktop/issues/new/choose' class='zen-link ml-1'>ご報告ください</a>。"
|
||||||
},
|
},
|
||||||
"platformNames": {
|
"platformNames": {
|
||||||
"mac": "macOS",
|
"mac": "macOS",
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"en": { "label": "English", "intl": "en-US" },
|
|
||||||
"ja": { "label": "日本語", "intl": "ja-JP" },
|
|
||||||
"es": { "label": "Español", "intl": "es-ES" }
|
|
||||||
}
|
|
|
@ -7,13 +7,12 @@ import InfoIcon from '~/icons/InfoIcon.astro'
|
||||||
import Layout from '~/layouts/Layout.astro'
|
import Layout from '~/layouts/Layout.astro'
|
||||||
import { getAllMods, getAuthorLink, getLocalizedDate } from '~/mods'
|
import { getAllMods, getAuthorLink, getLocalizedDate } from '~/mods'
|
||||||
import { getPath, getUI } from '~/utils/i18n'
|
import { getPath, getUI } from '~/utils/i18n'
|
||||||
import { getLocale, otherLocales } from '~/utils/i18n'
|
import { getLocale, getOtherLocales } from '~/utils/i18n'
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const mods = await getAllMods()
|
const mods = await getAllMods()
|
||||||
|
|
||||||
return mods.flatMap(mod => [
|
return mods.flatMap(mod => [
|
||||||
...otherLocales.map(locale => ({
|
...getOtherLocales().map(locale => ({
|
||||||
params: {
|
params: {
|
||||||
slug: mod.id,
|
slug: mod.id,
|
||||||
locale: locale,
|
locale: locale,
|
||||||
|
|
|
@ -1,444 +0,0 @@
|
||||||
import { type } from 'arktype'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines the schema for the i18n object.
|
|
||||||
*/
|
|
||||||
export const i18nSchema = type
|
|
||||||
.scope({
|
|
||||||
Title: type({
|
|
||||||
text: 'string',
|
|
||||||
highlight: 'boolean',
|
|
||||||
}),
|
|
||||||
|
|
||||||
Hero: {
|
|
||||||
title: 'Title[]',
|
|
||||||
description: 'string[]',
|
|
||||||
buttons: {
|
|
||||||
beta: 'string',
|
|
||||||
support: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
Feature: type({
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
}),
|
|
||||||
|
|
||||||
Sponsor: type({
|
|
||||||
name: 'string',
|
|
||||||
url: 'string',
|
|
||||||
}),
|
|
||||||
|
|
||||||
ValuesListEntry: type({
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
}),
|
|
||||||
|
|
||||||
Image: type({
|
|
||||||
alt: 'string',
|
|
||||||
}),
|
|
||||||
|
|
||||||
Member: type({
|
|
||||||
name: 'string',
|
|
||||||
description: 'string',
|
|
||||||
link: 'string|false',
|
|
||||||
}),
|
|
||||||
|
|
||||||
Donation: type({
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
button: 'string',
|
|
||||||
}),
|
|
||||||
|
|
||||||
Routes: {
|
|
||||||
index: {
|
|
||||||
title: 'string',
|
|
||||||
hero: 'Hero',
|
|
||||||
features: {
|
|
||||||
titles: 'string[]',
|
|
||||||
description: 'string',
|
|
||||||
featureTabs: {
|
|
||||||
workspaces: 'Feature',
|
|
||||||
compactMode: 'Feature',
|
|
||||||
glance: 'Feature',
|
|
||||||
splitView: 'Feature',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
sponsors: {
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
sponsors: {
|
|
||||||
tuta: 'Sponsor',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
community: {
|
|
||||||
title: 'string[]',
|
|
||||||
description: 'string',
|
|
||||||
lists: {
|
|
||||||
freeAndOpenSource: 'ValuesListEntry',
|
|
||||||
simpleYetPowerful: 'ValuesListEntry',
|
|
||||||
privateAndAlwaysUpToDate: 'ValuesListEntry',
|
|
||||||
},
|
|
||||||
images: {
|
|
||||||
community: 'Image',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mods: {
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
pagination: {
|
|
||||||
pagination: 'string',
|
|
||||||
},
|
|
||||||
search: 'string',
|
|
||||||
by: 'string',
|
|
||||||
sort: {
|
|
||||||
lastCreated: 'string',
|
|
||||||
lastUpdated: 'string',
|
|
||||||
perPage: 'string',
|
|
||||||
},
|
|
||||||
noResults: 'string',
|
|
||||||
noResultsDescription: 'string',
|
|
||||||
slug: {
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
alert: {
|
|
||||||
description: 'string',
|
|
||||||
button: 'string',
|
|
||||||
},
|
|
||||||
createdBy: 'string',
|
|
||||||
creationDate: 'string',
|
|
||||||
latestUpdate: 'string',
|
|
||||||
visitModHomepage: 'string',
|
|
||||||
installMod: 'string',
|
|
||||||
uninstallMod: 'string',
|
|
||||||
back: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
releaseNotes: {
|
|
||||||
topSection: {
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
},
|
|
||||||
list: {
|
|
||||||
support: 'string',
|
|
||||||
navigateToVersion: 'string',
|
|
||||||
},
|
|
||||||
itemType: {
|
|
||||||
fix: 'string',
|
|
||||||
feature: 'string',
|
|
||||||
known: 'string',
|
|
||||||
break: 'string',
|
|
||||||
theme: 'string',
|
|
||||||
security: 'string',
|
|
||||||
change: 'string',
|
|
||||||
},
|
|
||||||
backToTop: 'string',
|
|
||||||
chooseVersion: 'string',
|
|
||||||
components: {
|
|
||||||
releaseNoteItem: {
|
|
||||||
twilight: 'string',
|
|
||||||
twilightChanges: 'string',
|
|
||||||
releaseChanges: 'string',
|
|
||||||
firefoxVersion: 'string',
|
|
||||||
githubRelease: 'string',
|
|
||||||
workflowRun: 'string',
|
|
||||||
compareChanges: 'string',
|
|
||||||
twilightWarning: 'string',
|
|
||||||
reportIssues: 'string',
|
|
||||||
learnMore: 'string',
|
|
||||||
viewIssue: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
slug: {
|
|
||||||
title: 'string',
|
|
||||||
redirect: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
about: {
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
littleHelp: 'string',
|
|
||||||
mainTeam: {
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
subTitle: {
|
|
||||||
browser: 'string',
|
|
||||||
website: 'string',
|
|
||||||
},
|
|
||||||
members: {
|
|
||||||
browser: {
|
|
||||||
mauro: 'Member',
|
|
||||||
jan: 'Member',
|
|
||||||
bryan: 'Member',
|
|
||||||
oscar: 'Member',
|
|
||||||
daniel: 'Member',
|
|
||||||
brhm: 'Member',
|
|
||||||
kristijanribaric: 'Member',
|
|
||||||
larvey: 'Member',
|
|
||||||
studio: 'Member',
|
|
||||||
},
|
|
||||||
website: {
|
|
||||||
taroj1205: 'Member',
|
|
||||||
jace: 'Member',
|
|
||||||
canoa: 'Member',
|
|
||||||
adam: 'Member',
|
|
||||||
n7itro: 'Member',
|
|
||||||
jafeth: 'Member',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
contributors: {
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
browser: 'string',
|
|
||||||
website: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
donate: {
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
patreon: 'Donation',
|
|
||||||
koFi: 'Donation',
|
|
||||||
},
|
|
||||||
download: {
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
twilightInfo: 'string',
|
|
||||||
alertInfo: {
|
|
||||||
description: 'string',
|
|
||||||
},
|
|
||||||
platformSelector: {
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
},
|
|
||||||
additionalResources: {
|
|
||||||
title: 'string',
|
|
||||||
sourceCode: {
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
},
|
|
||||||
documentation: {
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
securityNotice: {
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
},
|
|
||||||
platformNames: {
|
|
||||||
mac: 'string',
|
|
||||||
windows: 'string',
|
|
||||||
linux: 'string',
|
|
||||||
macDownload: 'string',
|
|
||||||
windowsDownload: 'string',
|
|
||||||
linuxDownload: 'string',
|
|
||||||
},
|
|
||||||
platformDescriptions: {
|
|
||||||
mac: 'string',
|
|
||||||
windows: 'string',
|
|
||||||
linux: 'string',
|
|
||||||
},
|
|
||||||
links: {
|
|
||||||
macos: {
|
|
||||||
universal: 'string',
|
|
||||||
},
|
|
||||||
windows: {
|
|
||||||
'64bit': 'string',
|
|
||||||
ARM64: 'string',
|
|
||||||
},
|
|
||||||
linux: {
|
|
||||||
flathub: 'string',
|
|
||||||
x86_64: 'string',
|
|
||||||
aarch64: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
buttonCard: {
|
|
||||||
copy: 'string',
|
|
||||||
showChecksum: 'string',
|
|
||||||
beta: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
privacyPolicy: {
|
|
||||||
title: 'string',
|
|
||||||
lastUpdated: 'string',
|
|
||||||
sections: {
|
|
||||||
introduction: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
summary: 'string',
|
|
||||||
},
|
|
||||||
noCollect: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
},
|
|
||||||
noTelemetry: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
body2: 'string',
|
|
||||||
},
|
|
||||||
noPersonalData: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
},
|
|
||||||
noThirdParty: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
},
|
|
||||||
externalConnections: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
},
|
|
||||||
localStorage: {
|
|
||||||
title: 'string',
|
|
||||||
},
|
|
||||||
browsingData: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
},
|
|
||||||
cookies: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
},
|
|
||||||
cache: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
},
|
|
||||||
sync: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
link1: 'string',
|
|
||||||
link2: 'string',
|
|
||||||
},
|
|
||||||
addons: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
},
|
|
||||||
security: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
note: 'string',
|
|
||||||
},
|
|
||||||
control: {
|
|
||||||
title: 'string',
|
|
||||||
deletionTitle: 'string',
|
|
||||||
deletionBody: 'string',
|
|
||||||
},
|
|
||||||
website: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
externalLinksTitle: 'string',
|
|
||||||
externalLinksBody: 'string',
|
|
||||||
},
|
|
||||||
changes: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
},
|
|
||||||
otherTelemetry: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
firefoxPrivacyNotice: 'string',
|
|
||||||
forMoreInformation: 'string',
|
|
||||||
},
|
|
||||||
contact: {
|
|
||||||
title: 'string',
|
|
||||||
body: 'string',
|
|
||||||
discord: 'string',
|
|
||||||
discordLink: 'string',
|
|
||||||
github: 'string',
|
|
||||||
githubLink: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
welcome: {
|
|
||||||
title: 'string[]',
|
|
||||||
},
|
|
||||||
whatsNew: {
|
|
||||||
title: 'string',
|
|
||||||
reportIssue: 'string',
|
|
||||||
joinDiscord: 'string',
|
|
||||||
readFullReleaseNotes: 'string',
|
|
||||||
},
|
|
||||||
notFound: {
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
button: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
Page: type({
|
|
||||||
title: 'string',
|
|
||||||
description: 'string?',
|
|
||||||
}),
|
|
||||||
|
|
||||||
Layout: {
|
|
||||||
index: 'Page',
|
|
||||||
mods: 'Page',
|
|
||||||
releaseNotes: 'Page',
|
|
||||||
about: 'Page',
|
|
||||||
donate: 'Page',
|
|
||||||
download: 'Page',
|
|
||||||
privacyPolicy: 'Page',
|
|
||||||
welcome: 'Page',
|
|
||||||
whatsNew: 'Page',
|
|
||||||
},
|
|
||||||
|
|
||||||
Components: type({
|
|
||||||
footer: {
|
|
||||||
title: 'string',
|
|
||||||
description: 'string',
|
|
||||||
download: 'string',
|
|
||||||
followUs: 'string',
|
|
||||||
aboutUs: 'string',
|
|
||||||
teamAndContributors: 'string',
|
|
||||||
privacyPolicy: 'string',
|
|
||||||
getStarted: 'string',
|
|
||||||
documentation: 'string',
|
|
||||||
zenMods: 'string',
|
|
||||||
releaseNotes: 'string',
|
|
||||||
getHelp: 'string',
|
|
||||||
discord: 'string',
|
|
||||||
uptimeStatus: 'string',
|
|
||||||
reportAnIssue: 'string',
|
|
||||||
twilight: 'string',
|
|
||||||
madeWith: 'string',
|
|
||||||
},
|
|
||||||
nav: {
|
|
||||||
brand: 'string',
|
|
||||||
menu: {
|
|
||||||
gettingStarted: 'string',
|
|
||||||
usefulLinks: 'string',
|
|
||||||
mods: 'string',
|
|
||||||
download: 'string',
|
|
||||||
discord: 'string',
|
|
||||||
releaseNotes: 'string',
|
|
||||||
zenMods: 'string',
|
|
||||||
tryZenMods: 'string',
|
|
||||||
zenModsDesc: 'string',
|
|
||||||
releaseNotesDesc: 'string',
|
|
||||||
discordDesc: 'string',
|
|
||||||
donate: 'string',
|
|
||||||
donateDesc: 'string',
|
|
||||||
aboutUs: 'string',
|
|
||||||
aboutUsDesc: 'string',
|
|
||||||
documentation: 'string',
|
|
||||||
documentationDesc: 'string',
|
|
||||||
github: 'string',
|
|
||||||
githubDesc: 'string',
|
|
||||||
menu: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
I18n: {
|
|
||||||
routes: 'Routes',
|
|
||||||
layout: 'Layout',
|
|
||||||
components: 'Components',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.export('I18n')
|
|
|
@ -1,38 +0,0 @@
|
||||||
{
|
|
||||||
"macos": {
|
|
||||||
"universal": {
|
|
||||||
"label": "Universal",
|
|
||||||
"link": "https://github.com/zen-browser/desktop/releases/latest/download/zen.macos-universal.dmg"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"windows": {
|
|
||||||
"x86_64": {
|
|
||||||
"label": "64-bit (Recommended)",
|
|
||||||
"link": "https://github.com/zen-browser/desktop/releases/latest/download/zen.installer.exe"
|
|
||||||
},
|
|
||||||
"arm64": {
|
|
||||||
"label": "ARM64",
|
|
||||||
"link": "https://github.com/zen-browser/desktop/releases/latest/download/zen.installer-arm64.exe"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"linux": {
|
|
||||||
"x86_64": {
|
|
||||||
"tarball": {
|
|
||||||
"label": "Tarball",
|
|
||||||
"link": "https://github.com/zen-browser/desktop/releases/latest/download/zen.linux-x86_64.tar.xz"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"aarch64": {
|
|
||||||
"tarball": {
|
|
||||||
"label": "Tarball",
|
|
||||||
"link": "https://github.com/zen-browser/desktop/releases/latest/download/zen.linux-aarch64.tar.xz"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flathub": {
|
|
||||||
"all": {
|
|
||||||
"label": "Flathub",
|
|
||||||
"link": "https://flathub.org/apps/app.zen_browser.zen"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +1,13 @@
|
||||||
import { expect, test, type BrowserContextOptions, type Page } from '@playwright/test'
|
import { expect, test, type BrowserContextOptions, type Page } from '@playwright/test'
|
||||||
|
|
||||||
import checksumMock from './checksum-mock.json' with { type: 'json' }
|
import { getReleasesWithChecksums } from '~/components/download/release-data'
|
||||||
|
import { CONSTANT } from '~/constants'
|
||||||
|
|
||||||
/**
|
// Helper to get the platform section by id
|
||||||
* Helper to get the platform section by id.
|
|
||||||
*/
|
|
||||||
const getPlatformSection = (page: Page, platform: string) =>
|
const getPlatformSection = (page: Page, platform: string) =>
|
||||||
page.locator(`#${platform}-downloads.platform-section[data-active='true']`)
|
page.locator(`#${platform}-downloads.platform-section[data-active='true']`)
|
||||||
|
|
||||||
/**
|
// Helper to get the platform tab button
|
||||||
* Helper to get the platform tab button.
|
|
||||||
*/
|
|
||||||
const getPlatformButton = (page: Page, platform: string) =>
|
const getPlatformButton = (page: Page, platform: string) =>
|
||||||
page.locator(`button.platform-selector[data-platform='${platform}']`)
|
page.locator(`button.platform-selector[data-platform='${platform}']`)
|
||||||
|
|
||||||
|
@ -78,22 +75,24 @@ test.describe('Download page platform detection and tab switching', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Download page download links', () => {
|
test.describe('Download page download links', () => {
|
||||||
function getPlatformLinks() {
|
const releases = getReleasesWithChecksums('en')(CONSTANT.CHECKSUMS)
|
||||||
|
|
||||||
|
type Releases = ReturnType<ReturnType<typeof getReleasesWithChecksums>>
|
||||||
|
function getPlatformLinks(releases: Releases) {
|
||||||
return {
|
return {
|
||||||
mac: [checksumMock.macos.universal],
|
mac: [releases.macos.universal],
|
||||||
windows: [checksumMock.windows.x86_64, checksumMock.windows.arm64],
|
windows: [releases.windows.x86_64, releases.windows.arm64],
|
||||||
linux: [
|
linux: [
|
||||||
checksumMock.linux.x86_64.tarball,
|
releases.linux.x86_64.tarball,
|
||||||
checksumMock.linux.aarch64.tarball,
|
releases.linux.aarch64.tarball,
|
||||||
checksumMock.linux.flathub.all,
|
releases.linux.flathub.all,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test('all platform download links are correct', async ({ page }) => {
|
test('all platform download links are correct', async ({ page }) => {
|
||||||
const platforms = ['windows', 'mac', 'linux']
|
const platforms = ['windows', 'mac', 'linux']
|
||||||
const platformLinkSelectors = getPlatformLinks()
|
const platformLinkSelectors = getPlatformLinks(releases)
|
||||||
|
|
||||||
await page.goto('/download')
|
await page.goto('/download')
|
||||||
await page.waitForLoadState('domcontentloaded')
|
await page.waitForLoadState('domcontentloaded')
|
||||||
for (const platform of platforms) {
|
for (const platform of platforms) {
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { type i18nSchema } from '~/schemas/i18n'
|
|
||||||
|
|
||||||
import type languages from '~/i18n/locales.json'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the inferred type of the I18n schema.
|
|
||||||
*/
|
|
||||||
export type I18nType = typeof i18nSchema.I18n.infer
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of all supported locale keys as defined in locales.json.
|
|
||||||
*/
|
|
||||||
export type Locale = keyof typeof languages
|
|
|
@ -1,13 +1,21 @@
|
||||||
import { type AstroGlobal, type GetStaticPaths } from 'astro'
|
import { type AstroGlobal, type GetStaticPaths } from 'astro'
|
||||||
|
|
||||||
import { CONSTANT } from '~/constants'
|
import { CONSTANT } from '~/constants'
|
||||||
import { locales, translations } from '~/constants/i18n'
|
import { type UIProps } from '~/constants/i18n'
|
||||||
import { type I18nType, type Locale } from '~/types/i18n'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a localized path by prefixing the locale if necessary.
|
* Represents the available locales in the application
|
||||||
*/
|
*/
|
||||||
export const getPath = (locale?: Locale) => (path: string) => {
|
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): ((arg0: string) => string) =>
|
||||||
|
(path: string) => {
|
||||||
// Return external URLs unchanged
|
// Return external URLs unchanged
|
||||||
if (path.startsWith('http://') || path.startsWith('https://')) {
|
if (path.startsWith('http://') || path.startsWith('https://')) {
|
||||||
return path
|
return path
|
||||||
|
@ -33,27 +41,53 @@ export const getPath = (locale?: Locale) => (path: string) => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the current locale from the Astro object.
|
* Retrieves the current locale from the Astro object.
|
||||||
|
*
|
||||||
|
* @param Astro - The Astro object containing the current locale information
|
||||||
|
* @param Astro.currentLocale - The current locale string from Astro
|
||||||
|
* @returns The current locale cast as a Locale type
|
||||||
*/
|
*/
|
||||||
export const getLocale = (Astro: AstroGlobal): Locale => {
|
export const getLocale = (Astro: AstroGlobal): Locale => {
|
||||||
return Astro.currentLocale as Locale
|
return Astro.currentLocale as Locale
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of locales excluding the default locale.
|
* List of all supported locales
|
||||||
*/
|
*/
|
||||||
export const otherLocales = locales.filter(locale => locale !== CONSTANT.I18N.DEFAULT_LOCALE)
|
export const locales = CONSTANT.I18N.LOCALES.map(({ value }) => value)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively checks for missing keys in the translation objects.
|
* List of locales excluding the default locale
|
||||||
*/
|
*/
|
||||||
export function checkMismatch(
|
const otherLocales = CONSTANT.I18N.LOCALES.filter(
|
||||||
defaultObj: I18nType,
|
({ value }) => value !== CONSTANT.I18N.DEFAULT_LOCALE
|
||||||
localeObj: Partial<I18nType> = {},
|
).map(({ value }) => value)
|
||||||
path: string[] = [],
|
|
||||||
validLocale: Locale
|
/**
|
||||||
|
* Retrieves locales other than the default locale
|
||||||
|
* @returns {Locale[]} Array of non-default locales
|
||||||
|
*/
|
||||||
|
export const getOtherLocales = (): Locale[] => otherLocales
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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): UIProps => {
|
||||||
|
const validLocale = locales.includes(locale as Locale) ? locale : CONSTANT.I18N.DEFAULT_LOCALE
|
||||||
|
const defaultUI = CONSTANT.I18N.LOCALES.find(
|
||||||
|
({ value }) => value === CONSTANT.I18N.DEFAULT_LOCALE
|
||||||
|
)?.ui
|
||||||
|
const localeUI = CONSTANT.I18N.LOCALES.find(({ value }) => value === validLocale)?.ui
|
||||||
|
|
||||||
|
// Helper to recursively check for missing keys
|
||||||
|
function checkMismatch(
|
||||||
|
defaultObj: UIProps,
|
||||||
|
localeObj: Partial<UIProps> = {},
|
||||||
|
path: string[] = []
|
||||||
): void {
|
): void {
|
||||||
if (typeof defaultObj !== 'object' || defaultObj === null) return
|
if (typeof defaultObj !== 'object' || defaultObj === null) return
|
||||||
for (const key of Object.keys(defaultObj) as (keyof I18nType)[]) {
|
for (const key of Object.keys(defaultObj) as (keyof UIProps)[]) {
|
||||||
if (!(key in localeObj)) {
|
if (!(key in localeObj)) {
|
||||||
console.error(
|
console.error(
|
||||||
`[i18n] Missing translation key: ${[...path, key as string].join('.')} in locale '\x1b[1m${validLocale}\x1b[0m'. See src/i18n/${validLocale}/translation.json`
|
`[i18n] Missing translation key: ${[...path, key as string].join('.')} in locale '\x1b[1m${validLocale}\x1b[0m'. See src/i18n/${validLocale}/translation.json`
|
||||||
|
@ -65,28 +99,18 @@ export function checkMismatch(
|
||||||
localeObj[key] !== null
|
localeObj[key] !== null
|
||||||
) {
|
) {
|
||||||
// @ts-expect-error: recursive structure
|
// @ts-expect-error: recursive structure
|
||||||
checkMismatch(defaultObj[key], localeObj[key], [...path, key as string], validLocale)
|
checkMismatch(defaultObj[key], localeObj[key], [...path, key as string])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Deep merge: localeUI overrides defaultUI, fallback to defaultUI for missing keys
|
||||||
* Deep merges two translation objects.
|
function deepMerge(defaultObj: UIProps, localeObj: Partial<UIProps> = {}): UIProps {
|
||||||
* localeUI overrides defaultUI, fallback to defaultUI for missing keys.
|
if (typeof defaultObj !== 'object' || defaultObj === null) return defaultObj
|
||||||
*/
|
if (typeof localeObj !== 'object' || localeObj === null) return defaultObj
|
||||||
export function deepMerge(defaultObj: I18nType, localeObj: Partial<I18nType> = {}): I18nType {
|
|
||||||
if (
|
|
||||||
typeof defaultObj !== 'object' ||
|
|
||||||
defaultObj === null ||
|
|
||||||
typeof localeObj !== 'object' ||
|
|
||||||
localeObj === null
|
|
||||||
) {
|
|
||||||
return defaultObj
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const result: any = Array.isArray(defaultObj) ? [...defaultObj] : { ...defaultObj }
|
const result: any = Array.isArray(defaultObj) ? [...defaultObj] : { ...defaultObj }
|
||||||
for (const key of Object.keys(defaultObj) as (keyof I18nType)[]) {
|
for (const key of Object.keys(defaultObj) as (keyof UIProps)[]) {
|
||||||
if (key in localeObj) {
|
if (key in localeObj) {
|
||||||
if (
|
if (
|
||||||
typeof defaultObj[key] === 'object' &&
|
typeof defaultObj[key] === 'object' &&
|
||||||
|
@ -106,26 +130,22 @@ export function deepMerge(defaultObj: I18nType, localeObj: Partial<I18nType> = {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Default UI translation for the default locale.
|
|
||||||
*/
|
|
||||||
const defaultUI = translations[CONSTANT.I18N.DEFAULT_LOCALE]
|
|
||||||
|
|
||||||
if (!defaultUI) {
|
if (!defaultUI) {
|
||||||
throw new Error('Default UI translation is missing!')
|
throw new Error('Default UI translation is missing!')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (localeUI && validLocale !== CONSTANT.I18N.DEFAULT_LOCALE) {
|
||||||
* Retrieves UI translations for a given locale.
|
checkMismatch(defaultUI, localeUI)
|
||||||
*/
|
return deepMerge(defaultUI, localeUI) as UIProps
|
||||||
export const getUI = (locale?: Locale): I18nType => {
|
}
|
||||||
const validLocale = locale && locales.includes(locale) ? locale : CONSTANT.I18N.DEFAULT_LOCALE
|
|
||||||
|
|
||||||
return translations[validLocale] ?? defaultUI
|
// If localeUI is undefined or locale is default, just return defaultUI
|
||||||
|
return defaultUI
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates static paths for internationalization.
|
* Generates static paths for internationalization
|
||||||
|
* @returns {Array} An array of static paths for different locales
|
||||||
*/
|
*/
|
||||||
export const getStaticPaths = (() => {
|
export const getStaticPaths = (() => {
|
||||||
return [
|
return [
|
||||||
|
@ -133,11 +153,21 @@ export const getStaticPaths = (() => {
|
||||||
params: { locale: undefined },
|
params: { locale: undefined },
|
||||||
props: { locale: CONSTANT.I18N.DEFAULT_LOCALE },
|
props: { locale: CONSTANT.I18N.DEFAULT_LOCALE },
|
||||||
},
|
},
|
||||||
...otherLocales.map(locale => ({
|
...CONSTANT.I18N.LOCALES.filter(({ value }) => value !== CONSTANT.I18N.DEFAULT_LOCALE).map(
|
||||||
params: { locale },
|
({ value }) => ({
|
||||||
|
params: { locale: value },
|
||||||
props: {
|
props: {
|
||||||
locale,
|
locale: value,
|
||||||
},
|
},
|
||||||
})),
|
})
|
||||||
|
),
|
||||||
]
|
]
|
||||||
}) satisfies GetStaticPaths
|
}) satisfies GetStaticPaths
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves all available locales, including both default and non-default
|
||||||
|
* @returns {Locale[]} Combined array of all locales
|
||||||
|
*/
|
||||||
|
export const getLocales = (): Locale[] => {
|
||||||
|
return [...locales, ...otherLocales]
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue