From 76741c5b5982de9dc6b93715cdcd5dd7cde0ecf7 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Wed, 11 Sep 2024 03:40:08 -0600 Subject: [PATCH 001/493] i18n(es): update `customization` (#2312) --- docs/src/content/docs/es/guides/customization.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/src/content/docs/es/guides/customization.mdx b/docs/src/content/docs/es/guides/customization.mdx index 67f40b04ad6..44f367bd218 100644 --- a/docs/src/content/docs/es/guides/customization.mdx +++ b/docs/src/content/docs/es/guides/customization.mdx @@ -106,6 +106,8 @@ export default defineConfig({ }); ``` +Aprende cómo [agregar un enlace del mapa del sitio a `robots.txt`](https://docs.astro.build/es/guides/integrations-guide/sitemap/#enlace-de-sitemap-en-robotstxt) en la documentación de Astro. + ## Diseño de página De forma predeterminada, las páginas de Starlight utilizan un diseño con una barra lateral de navegación global y una tabla de contenidos que muestra los encabezados de la página actual. @@ -376,7 +378,7 @@ Proporciona módulos npm que puedes instalar para las fuentes que deseas utiliza 2. Instala el paquete para la fuente que has elegido. Puedes encontrar el nombre del paquete haciendo clic en “Install” en la página de la fuente de Fontsource. - + From db86cd43ce56969f436ff9893ae815943eb10396 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Wed, 11 Sep 2024 03:45:14 -0600 Subject: [PATCH 002/493] i18n(es): add `authoring-content` (#2320) --- ...oring-content.md => authoring-content.mdx} | 143 ++++++++++++++++-- 1 file changed, 129 insertions(+), 14 deletions(-) rename docs/src/content/docs/es/guides/{authoring-content.md => authoring-content.mdx} (84%) diff --git a/docs/src/content/docs/es/guides/authoring-content.md b/docs/src/content/docs/es/guides/authoring-content.mdx similarity index 84% rename from docs/src/content/docs/es/guides/authoring-content.md rename to docs/src/content/docs/es/guides/authoring-content.mdx index 231b3c30c1a..1d835813071 100644 --- a/docs/src/content/docs/es/guides/authoring-content.md +++ b/docs/src/content/docs/es/guides/authoring-content.mdx @@ -202,8 +202,8 @@ Un bloque de código se indica con un bloque de tres comillas invertidas ` ```js // Código JavaScript con resaltado de sintaxis. var fun = function lang(l) { - dateformat.i18n = require('./lang/' + l); - return true; + dateformat.i18n = require('./lang/' + l); + return true; }; ``` @@ -211,8 +211,8 @@ var fun = function lang(l) { ```js // Código JavaScript con resaltado de sintaxis. var fun = function lang(l) { - dateformat.i18n = require('./lang/' + l); - return true; + dateformat.i18n = require('./lang/' + l); + return true; }; ``` ```` @@ -248,8 +248,8 @@ Algunos de los ejemplos más comunes se muestran a continuación: ````md ```js {2-3} function demo() { - // Esta línea (#2) y la siguiente están resaltadas - return 'Esta es la línea #3 de este fragmento'; + // Esta línea (#2) y la siguiente están resaltadas + return 'Esta es la línea #3 de este fragmento'; } ``` ```` @@ -259,7 +259,7 @@ Algunos de los ejemplos más comunes se muestran a continuación: ```js "Términos individuales" /También.*compatibles/ // Términos individuales también pueden ser resaltados function demo() { - return 'También las expresiones regulares son compatibles'; + return 'También las expresiones regulares son compatibles'; } ``` @@ -267,7 +267,7 @@ Algunos de los ejemplos más comunes se muestran a continuación: ```js "Términos individuales" /También.*compatibles/ // Términos individuales también pueden ser resaltados function demo() { - return 'También las expresiones regulares son compatibles'; + return 'También las expresiones regulares son compatibles'; } ``` ```` @@ -276,18 +276,18 @@ Algunos de los ejemplos más comunes se muestran a continuación: ```js "return true;" ins="insertados" del="eliminados" function demo() { - console.log('Estos son tipos de marcadores insertados y eliminados'); - // La declaración de retorno utiliza el tipo de marcador predeterminado - return true; + console.log('Estos son tipos de marcadores insertados y eliminados'); + // La declaración de retorno utiliza el tipo de marcador predeterminado + return true; } ``` ````md ```js "return true;" ins="insertados" del="eliminados" function demo() { - console.log('Estos son tipos de marcadores insertados y eliminados'); - // La declaración de retorno utiliza el tipo de marcador predeterminado - return true; + console.log('Estos son tipos de marcadores insertados y eliminados'); + // La declaración de retorno utiliza el tipo de marcador predeterminado + return true; } ``` ```` @@ -392,3 +392,118 @@ Starlight admite todas las demás sintaxis de autoría de Markdown, como listas ## Configuración avanzada de Markdown y MDX Starlight utiliza el motor de renderizado de Markdown y MDX de Astro, construido sobre remark y rehype. Puedes añadir soporte para sintaxis y comportamientos personalizados añadiendo `remarkPlugins` o `rehypePlugins` en tu archivo de configuración de Astro. Consulta la sección ["Configuración de Markdown y MDX"](https://docs.astro.build/es/guides/markdown-content/#configuraci%C3%B3n-de-markdown-y-mdx) en la documentación de Astro para obtener más información. + +## Markdoc + +Starlight admite la creación de contenido en Markdoc utilizando la integración experimental de [Astro Markdoc](https://docs.astro.build/es/guides/integrations-guide/markdoc/) y el preset de Starlight Markdoc. + +### Crea un nuevo proyecto con Markdoc + +Empieza un nuevo proyecto en Starlight con Markdoc preconfigurado usando `create astro`: + +import { Tabs, TabItem, Steps } from '@astrojs/starlight/components'; + + + + +```sh +npm create astro@latest -- --template starlight/markdoc +``` + + + + +```sh +pnpm create astro --template starlight/markdoc +``` + + + + +```sh +yarn create astro --template starlight/markdoc +``` + + + + +### Agrega Markdoc a un proyecto existente + +Si ya tienes un sitio Starlight y quieres agregar Markdoc, sigue estos pasos. + + + +1. Agrega la integración de Markdoc de Astro: + + + + + + ```sh + npx astro add markdoc + ``` + + + + + + ```sh + pnpm astro add markdoc + ``` + + + + + + ```sh + yarn astro add markdoc + ``` + + + + + +2. Instala el preajuste de Starlight Markdoc: + + + + + + ```sh + npm install @astrojs/starlight-markdoc + ``` + + + + + + ```sh + pnpm add @astrojs/starlight-markdoc + ``` + + + + + + ```sh + yarn add @astrojs/starlight-markdoc + ``` + + + + + +3. Crea un archivo de configuración de Markdoc en `markdoc.config.mjs` y utiliza el preset de Starlight Markdoc: + + ```js + import { defineMarkdocConfig } from '@astrojs/markdoc/config'; + import starlightMarkdoc from '@astrojs/starlight-markdoc'; + + export default defineMarkdocConfig({ + extends: [starlightMarkdoc()], + }); + ``` + + + +Para obtener más información sobre la sintaxis y las características de Markdoc, consulta la [documentación de Markdoc](https://markdoc.dev/docs/syntax) o la [guía de integración de Astro Markdoc](https://docs.astro.build/es/guides/integrations-guide/markdoc/). From b2958b7696d0d3a77c9c2dfa066ad52927e7c506 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Wed, 11 Sep 2024 03:47:32 -0600 Subject: [PATCH 003/493] i18n(es): update `index` (#2316) --- docs/src/content/docs/es/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/content/docs/es/index.mdx b/docs/src/content/docs/es/index.mdx index 457604b95ad..58d6acef025 100644 --- a/docs/src/content/docs/es/index.mdx +++ b/docs/src/content/docs/es/index.mdx @@ -71,7 +71,7 @@ import Testimonial from '~/components/testimonial.astro'; la atención a los detalles son inspiradores. Se encarga de la tecnología y el aspecto, para que puedas centrarte en tu contenido 👏 - ¡El equipo de StackBlitz lo ama absolutamente! + ¡El equipo de StackBlitz lo ama absolutamente! Date: Wed, 11 Sep 2024 09:48:13 +0000 Subject: [PATCH 004/493] [ci] format --- docs/src/content/docs/es/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/content/docs/es/index.mdx b/docs/src/content/docs/es/index.mdx index 58d6acef025..4d2515ab485 100644 --- a/docs/src/content/docs/es/index.mdx +++ b/docs/src/content/docs/es/index.mdx @@ -71,7 +71,7 @@ import Testimonial from '~/components/testimonial.astro'; la atención a los detalles son inspiradores. Se encarga de la tecnología y el aspecto, para que puedas centrarte en tu contenido 👏 - ¡El equipo de StackBlitz lo ama absolutamente! +¡El equipo de StackBlitz lo ama absolutamente! Date: Wed, 11 Sep 2024 03:49:54 -0600 Subject: [PATCH 005/493] i18n(es): update `site-search` (#2315) Co-authored-by: Chris Swithinbank --- docs/src/content/docs/es/guides/site-search.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/content/docs/es/guides/site-search.mdx b/docs/src/content/docs/es/guides/site-search.mdx index a100172a2d8..144c32a5a5a 100644 --- a/docs/src/content/docs/es/guides/site-search.mdx +++ b/docs/src/content/docs/es/guides/site-search.mdx @@ -52,7 +52,7 @@ Si tienes acceso al [programa DocSearch de Algolia](https://docsearch.algolia.co 1. Instala `@astrojs/starlight-docsearch`: - + From d8aa075c62964a8f07f9894d9eb1df186dd1d092 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Wed, 11 Sep 2024 03:57:55 -0600 Subject: [PATCH 006/493] i18n(es): update `pages` (#2314) Co-authored-by: Chris Swithinbank --- docs/src/content/docs/es/guides/pages.mdx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/src/content/docs/es/guides/pages.mdx b/docs/src/content/docs/es/guides/pages.mdx index 7506a490759..e99d7c2716d 100644 --- a/docs/src/content/docs/es/guides/pages.mdx +++ b/docs/src/content/docs/es/guides/pages.mdx @@ -14,7 +14,7 @@ Esta guía muestra cómo funciona la generación de páginas en Starlight. ### Formatos de archivo Starlight admite la creación de contenido en Markdown y MDX sin necesidad de configuración. -Puedes agregar soporte para Markdoc instalando la integración experimental de [Astro Markdoc](https://docs.astro.build/es/guides/integrations-guide/markdoc/). +Puedes agregar soporte para Markdoc siguiendo la [guía de “Markdoc”](/es/guides/authoring-content/#markdoc). ### Agregar páginas @@ -109,26 +109,25 @@ Las siguientes propiedades difieren del frontmatter de Markdown: ##### `sidebar` -**tipo:** `SidebarEntry[]` +**tipo:** [`SidebarItem[]`](/es/reference/configuration/#sidebaritem) **por defecto:** la barra lateral generada basada en la [configuración global de `sidebar`](/es/reference/configuration/#sidebar) Proporciona una barra lateral de navegación personalizada para esta página. Si no se establece, la página usará la barra lateral global predeterminada. -Por ejemplo, la siguiente página sobrescribe la barra lateral predeterminada con un enlace a la página de inicio y un grupo de enlaces a diferentes constelaciones. -La página actual en la barra lateral se establece utilizando la propiedad `isCurrent` y se ha agregado un `badge` opcional a un elemento de enlace. +Por ejemplo, la siguiente página anula la barra lateral predeterminada con un enlace a la página de inicio y un grupo de enlaces a varias otras páginas personalizadas. ```astro {3-13} ``` +Ver la guía [“Navegación de la barra lateral”](/es/guides/sidebar/) para obtener más información sobre las opciones disponibles para personalizar la barra lateral. + ##### `hasSidebar` **tipo:** `boolean` From c11641709156d976e777ef20b1f4fccc185e521b Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Wed, 11 Sep 2024 04:02:19 -0600 Subject: [PATCH 007/493] i18n(es): update `plugins` (#2319) Co-authored-by: Chris Swithinbank --- .../src/content/docs/es/resources/plugins.mdx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/src/content/docs/es/resources/plugins.mdx b/docs/src/content/docs/es/resources/plugins.mdx index 5f0f1fff6f9..dcc06c3a30e 100644 --- a/docs/src/content/docs/es/resources/plugins.mdx +++ b/docs/src/content/docs/es/resources/plugins.mdx @@ -78,6 +78,23 @@ Amplia tu sitio con un plugins oficiales respaldados por el equipo de Starlight title="starlight-versions" description="Agrega versiones a tus páginas de documentación de Starlight." /> + + + + +### Temas de la comunidad + +Un tema es un plugin de Starlight que cambia la apariencia visual de un sitio con reemplazos de componentes, CSS personalizado u otras nuevas características. + + + From ea2b1bde9d926e3fa9c3e5d17badc6e0a9ff8700 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Wed, 11 Sep 2024 04:04:41 -0600 Subject: [PATCH 008/493] i18n(es): update `frontmatter` (#2318) Co-authored-by: Chris Swithinbank --- docs/src/content/docs/es/reference/frontmatter.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/content/docs/es/reference/frontmatter.md b/docs/src/content/docs/es/reference/frontmatter.md index d9f360733dd..ee1dc616887 100644 --- a/docs/src/content/docs/es/reference/frontmatter.md +++ b/docs/src/content/docs/es/reference/frontmatter.md @@ -115,10 +115,10 @@ hero: - text: Cuéntame más link: /getting-started/ icon: right-arrow - variant: primary - text: View on GitHub link: https://github.com/astronaut/my-project icon: external + variant: minimal attrs: rel: me --- @@ -166,8 +166,8 @@ interface HeroConfig { actions?: Array<{ text: string; link: string; - variant: 'primary' | 'secondary' | 'minimal'; - icon: string; + variant?: 'primary' | 'secondary' | 'minimal'; + icon?: string; attrs?: Record; }>; } From f3f270e74911ec6d077a567099ce09d5ef37c83b Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Wed, 11 Sep 2024 07:33:57 -0600 Subject: [PATCH 009/493] i18n(es): update `components` (#2310) Co-authored-by: Chris Swithinbank --- .../src/content/docs/es/guides/components.mdx | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/docs/src/content/docs/es/guides/components.mdx b/docs/src/content/docs/es/guides/components.mdx index 3352d96c73e..99c4ede6c6f 100644 --- a/docs/src/content/docs/es/guides/components.mdx +++ b/docs/src/content/docs/es/guides/components.mdx @@ -88,7 +88,7 @@ El código anterior genera las siguientes pestañas en la página: Manten varias pestañas sincronizadas añadiendo el atributo `syncKey`. -Todas las `` en una página con el mismo valor de `syncKey` mostrarán la misma etiqueta activa. Esto permite a tu lector elegir una vez (por ejemplo, su sistema operativo o gestor de paquetes) y ver su elección reflejada en toda la página. +Todas las `` en una página con el mismo valor de `syncKey` mostrarán la misma etiqueta activa. Esto permite a tu lector elegir una vez (por ejemplo, su sistema operativo o gestor de paquetes) y ver su elección persistida en toda la navegación de la página. Para sincronizar pestañas relacionadas, añade una propiedad `syncKey` idéntica a cada componente `` y asegúrate de que todos usen las mismas etiquetas ``: @@ -227,6 +227,38 @@ import { LinkCard } from '@astrojs/starlight/components'; +### Botones de enlace + +Usa el componente `` para enlaces de llamada a la acción visualmente distintos. +Un botón de enlace es útil para dirigir a los usuarios al contenido más relevante o contenido accionable y se usa a menudo en páginas de destino + +Un `` requiere un atributo [`href`](https://developer.mozilla.org/es/docs/Web/HTML/Element/a#href) y opcionalmente acepta otros atributos de enlace como `target`. + +El atributo `icon` se puede establecer opcionalmente en el nombre de [uno de los iconos integrados de Starlight](#todos-los-iconos) para incluir un icono junto al texto. +El atributo ìconPlacemente`se puede usar para colocar el icono antes del texto estableciéndolo en`start`(el valor predeterminado es`end`). + +Personaliza la apariencia del botón de enlace utilizando el atributo `variant`, que se puede establecer en `primary` (el valor predeterminado), `secondary` o `minimal`. + +```mdx +# src/content/docs/example.mdx + +import { LinkButton } from '@astrojs/starlight/components'; + +Comienza + + Relacionado: Astro + +``` + +El código anterior genera lo siguiente en la página: + +import { LinkButton } from '@astrojs/starlight/components'; + +Comienza + + Relacionado: Astro + + ### Apartados Los apartados son útiles para mostrar información secundaria junto al contenido principal de una página. From a44b3044107c79167ffebfb7a14354f600138424 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Wed, 11 Sep 2024 08:18:58 -0600 Subject: [PATCH 010/493] i18n(es): update `css-and-tailwind` (#2311) Co-authored-by: Chris Swithinbank --- docs/src/content/docs/es/guides/css-and-tailwind.mdx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/src/content/docs/es/guides/css-and-tailwind.mdx b/docs/src/content/docs/es/guides/css-and-tailwind.mdx index 418d67b92bc..bf37249db08 100644 --- a/docs/src/content/docs/es/guides/css-and-tailwind.mdx +++ b/docs/src/content/docs/es/guides/css-and-tailwind.mdx @@ -251,6 +251,8 @@ Estas variables son utilizadas en toda la UI con una gama de tonos grises utiliz Usa los controles deslizantes a continuación para modificar las paletas de colores de acento y gris de Starlight. Las áreas de vista previa oscura y clara mostrarán los colores resultantes, y toda la página también se actualizará para obtener una vista previa de tus cambios. +Usa la opción Nivel de Contraste para especificar qué estándares de contraste de color de las [pautas de accesibilidad de contenido web](https://developer.mozilla.org/es/docs/Web/Accessibility/Understanding_WCAG/Perceivable/Color_contrast) debe cumplir. + Cuando estés satisfecho con tus cambios, copia el código CSS o Tailwind a continuación y úsalo en tu proyecto. import ThemeDesigner from '~/components/theme-designer.astro'; @@ -266,6 +268,9 @@ import ThemeDesigner from '~/components/theme-designer.astro'; default: 'Por defecto', random: 'Aleatorio', }, + contrast: { + label: 'Nivel de Contraste', + }, editor: { accentColor: 'Acento', grayColor: 'Gris', From 0184cfc53797f54f50fc8031a9324830866c7117 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Wed, 11 Sep 2024 14:23:11 -0600 Subject: [PATCH 011/493] i18n(es): update `manual-setup` & `configuration` (#2317) Co-authored-by: Chris Swithinbank --- docs/src/content/docs/es/manual-setup.mdx | 8 +++----- docs/src/content/docs/es/reference/configuration.mdx | 12 ++++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/src/content/docs/es/manual-setup.mdx b/docs/src/content/docs/es/manual-setup.mdx index 7fa2377b041..274d3ef3913 100644 --- a/docs/src/content/docs/es/manual-setup.mdx +++ b/docs/src/content/docs/es/manual-setup.mdx @@ -16,7 +16,7 @@ Para seguir esta guía, necesitarás un proyecto de Astro existente. Starlight es una [integración de Astro](https://docs.astro.build/es/guides/integrations-guide/). Agregala a tu sitio ejecutando el comando `astro add` en el directorio raíz de tu proyecto: - + ```sh npx astro add starlight @@ -125,8 +125,6 @@ En el futuro, planeamos soportar mejor este caso de uso para evitar la necesidad ### Usa Starlight con SSR -Por el momento, Starlight no soporta el [despliegue SSR](https://docs.astro.build/es/guides/server-side-rendering/) usando los adaptadores de servidor de Astro. Esperamos poder soportar esto pronto. +Para habilitar el SSR, sigue la guía [“Adaptadores de Renderizado bajo Demanda”](https://docs.astro.build/es/guides/server-side-rendering/) en la documentación de Astro para agregar un adaptador de servidor a tu proyecto Starlight. -Puedes usar Starlight junto a páginas personalizadas renderizadas bajo demanda en tu proyecto siguiendo la guía [“Adaptadores de Renderizado bajo Demanda”](https://docs.astro.build/es/guides/server-side-rendering/) en la documentación de Astro. - -Por el momento, las páginas de documentación generadas por Starlight siempre son prerenderizadas, independientemente del modo de salida de tu proyecto. Esperamos poder soportar el renderizado bajo demanda para las páginas de Starlight pronto. +Las páginas de documentación generadas por Starlight se pre-renderizan de forma predeterminada independientemente del modo de salida de tu proyecto. Para excluir tus páginas de Starlight de la pre-renderización, establece la [opción de configuración `prerender`](/es/reference/configuration/#prerender) en `false`. diff --git a/docs/src/content/docs/es/reference/configuration.mdx b/docs/src/content/docs/es/reference/configuration.mdx index db136656c80..1e000e643cc 100644 --- a/docs/src/content/docs/es/reference/configuration.mdx +++ b/docs/src/content/docs/es/reference/configuration.mdx @@ -455,6 +455,18 @@ Define si el proveedor de búsqueda predeterminado de Starlight [Pagefind](https Establece esta opción a `false` para excluir una página de los resultados de búsqueda. Esto también ocultará la interfaz de usuario de búsqueda predeterminada si está en uso. +Pagefind no puede estar habilitado cuando la opción [`prerender`](#prerender) está establecida en `false`. + +### `prerender` + +**tipo:** `boolean` +**por defecto:** `true` + +Define si las páginas de Starlight deben ser pre-renderizadas a HTML estático o renderizadas bajo demanda por un [adaptador SSR](https://docs.astro.build/es/guides/server-side-rendering/). + +Las páginas de Starlight se pre-renderizan de forma predeterminada. +Si estás usando un adaptador SSR y deseas renderizar las páginas de Starlight bajo demanda, establece `prerender: false`. + ### `head` **tipo:** [`HeadConfig[]`](#headconfig) From cfe54bc5e39033166ab956061583848a38e70f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20D=C3=A9ramond?= Date: Thu, 12 Sep 2024 22:50:27 +0200 Subject: [PATCH 012/493] docs(showcase): add Open {re}Source website (#2324) --- docs/src/assets/showcase/openresource.dev.png | Bin 0 -> 70055 bytes docs/src/components/showcase-sites.astro | 1 + 2 files changed, 1 insertion(+) create mode 100644 docs/src/assets/showcase/openresource.dev.png diff --git a/docs/src/assets/showcase/openresource.dev.png b/docs/src/assets/showcase/openresource.dev.png new file mode 100644 index 0000000000000000000000000000000000000000..f611966fdfbc3919892afcaa877e60d580173b8e GIT binary patch literal 70055 zcmeFZWl&aO_b*I{AdP@@i4sb8BPmFCcXxM6OAFG{64FR_Nq2Wk-gLuFoQvlqNd4zgON$Na4f`S? zP9iS5!~GgmKA(1)lG75Qeb{%HfdOLDhJ-)X$jTMZkNgeDI2@V22cr-;DB{!M|3#ts z)cd0IuYzbH?&IcaTd~z=r*`SEwc0XzGye&mF)xkTkp4*@7;Qb~;Td0n5AHef-^7ZS zG+r)@ct1tC<|$;I?Lv=DR?D?vN=K_9K5z$I`X9rJSvZt>KO3wT5ZjpK5y?lSd>AY8p(aaT4b~mN0>SKHB<}%cB#R zgsV^ZWqI`?Z#NVTb|bItgq9ilFz=rChx$Y2nc^gDSlr)yaw9%yf#?L}kHEC{B~IIx%A-Dh*aQ@NAIP`^1-R zHDGNt|9{uU-Nyd;Tm>r$IR-MJHbZcI0=^sf`=WPWm9p|*-Rck!zK)YT#r!m3sb&@G z+uFKE$uRxpkH`1v2zI#{x~7Fr+E$yYqu1OPL$%2ZOto|n(l!+gQ(5O)p%Yf}2J^vh z@!@EQvGMqBlINy-7F#$duknXvW`>G>xvsCqXJ^+NzSrpB_SRh@5nG}TK93c%q$7PN zi4WUjx0tp@t2_SYPm4WDfJCNdS-3KNe8ej_muS^>;n2=pm{z9xby1oiER3k5Bs`wb z02hQ(-AH!!%SqejE!b9`x>3U(w2>cbYbXMVaz{N!3W4&kv0d@8P=CRylUY1z(|S;DZ4KmEW3H*m^`+hjt*w;b+E7uR66!j_T9@{DN2j}TK9<4DGYvxl}!blWBn8IPXB@GR)F4gh= z`ywH5IgDE~+DZ-;*Lk1qXNk3K_Z6n1uwg@%+XOe6D5~(NK~!{i6a* zVmQ2e^c?JHZR+5nPb(b3$ATeY=D|uM6O#RWwXhOKMwHXjH81Aj!y?1Fj1zm}F_*U- z8w!8_&Mhek=28rTPtwG!NQxc-$lhMDlHk5q` z4$Sxo5h$jwE0v z5M*RzULYcpKn#&i(_Z5k_wHHq&2)nyI-owxIH9t$b1G@7KBl2npwEI4-|20PvkkHA zh}_4FvymJkz0;Cc$(uA#rJ8=%PYhP)-jp`n@WQ`757F4~Garls&L>!Bqlx+)e2r4J z(N@WS;f6yHdCj`X3(6!Ri1}^l=Uw6I9Y+s)t~C1w{`-lhRcjV3{I$^O#;0d)0ed6a+|o9?drROg)Ukn|D=oEBU3vn6 z{%^Uc)@F4h>Hu>4W=NQNm@FGyT$e??C-%T@Ywzw+>%tSE#nVddY*UTk)|gDx(ZOhw zZwlhRRmU=%svi>Sk#FGBr=7i0sRyP}HRiH{D)bUx{d2*{2Vx@|t8SA0M+?23&_+RY z$Rqduj>Sb5ap_@9UKhqE%~3fUAC{b=3Tnia2Rvo9PpcG6XU6HHE6Q_eDAz48T|Hd` z2ux7^zEN1SmVj>LYy}=8+2C_q|22D~*`kS4^vg!MI2Jy^!BtCEj8rpE2CpR z>dG1$DUT`>a*9_Fh$ndCZK)j*KXUXgOG&?5dsCOtxwGfZchb)>GjqlFMo6j-KDO5UPxi~Pj?w(d+Kl8}^go<5Ka%`& zWz9B6ksB6q0s=b>$2D4DGFs%atNZE+N;poYL?pip>vC{GoXegBk&xhuaiys?yB7aV zDJUaEZkVy{ z?YFHz6+-&9E4(&F$mV_e@}6(joCa|rO79i!`&ecqhGv3^m(JO+#ev<()40eeZ9#L! z<`yBeYC=aZ-60Oa@gq}}cP@w8D!6C0ot$LC@<_^wAK7XKBeJc=++s)SStH5!S3&^Rpp5g7mt=iiU3=?o4i9;1w19G3@@rM;djy zDJMG1#->wHR)$_MDXFc!WH5TW)Ha(}+`_bs{)x|a6TZnL%HivVCxK3cjU1;8m61 z+gCH|>(yP426D&eCB#j6(9qCpZHB4X-xJ^jx26!Nm&WO75tE83%w<%i-`{(KZ6EgS zo9S$s&du#@e8vi5({l9Wp7C%hdr)v_rTw~aPZUu^QBhlJnLdT+M}!%=fYDV|u1oE7 z$)sKPrJhv1RHC!G&a@{s&zB=lB%#D@zltjEy^?Rk1G>f=Js{0UICBZ`q%kAQa)Fof z{P6gP5nz|9yh^6$Axff6rmwir+7)~~Qm@!yyzhA&nNJ`$Hrk+Rwr)N5M|OTzshF^P zZ@*PDHF7OWt#O3iFW`@G`j|v;s$+VVe6SkX%zOoEPrAdd&u`=xFY(ce>8)^e{e}v2 zLp0N8$&}{Oi?dSO!4{pWDk&6~o9TIPKYna5_~B+lCM+`8$&L*!azu)=^@F%d;za!E=Y>-ui`r6<&wN>?etLKMt4|A{%Xg+Kn@)-^3`oT%JU|z7_7?86&rP{ti(sMZte{0RiC!!u;=YJMt>Km$d!;D+;Vlu3MEJ=c?jSgnK@I(#~uh9YWRB z)v;+!fjvFqot<3((ER%K>qmI_mlBST+1V8DsNS6%x;f2*#k4AKN&WTfuWeFYLqkeW z4^?NUaLej7fGR;jFN|17_&yw|!sI|s9#JYzq-gro!XiUh88{i5SMCOXJ;<<}5~oMl zeO*K9TYRZCLl{U8MW0FWCb&TqGE5zpxm`h#SA$PW6j?Q3vPX^Nl`-p}3$_zfol7Zo zn&xM^f43RJo$*6Wxx#Cd)VA@I=Pe~A3_h&n$y_2ct*Q+@0IwzjGqrwPF(H`<`xa(9 zSOIS#3Lb@>s##pm>rkm9rVvgSvzy2OX1QE)JRCIfnAumz?AzjWP@z55i!B`zFpau)lX*T zw4tncF1aU{Sz#t2*YP(p&I`K`G+zcWt7*(;uS(Q!*xsC?+^kbpAQE)VmOaMH&PZ&9 zWfbIF+}zGfNI`}RGJR!1s9p8tOq!HUmjK(CG^UsLg{FdXCTe_RS(WFo=AFsqdtNWz zh$OoA3>=A!K{CV082${e-}q7lWx>AdIl`vZftAL)&bSKx|@iX^0^cW0daYju+? zCRcZgJ~9^QwbA+BJ5WUUYIfSRwFU3)76#VBHTdD>1_)|(|npB zZ>SqFar;zGxB04Tbaju7lc42oLmDn=tHQPHqhqx%`7#4#z%;Wbuk8)%lp2Iiudu?j zva^3@FESa1*bG)eN(!YBT{Ufb=W;DpKQ%QK{ifAvR3N_G%x`efPOjIw(tiFu-XEi0 z*?kBhm%$Q#YbI~z)}R{m?SgA6TUxF6N3=y^ZGVLHq?>rnIY{RGDs1ZgC6bYTUYxcIncK;y-- zvnnvXveH9d_Es|d!Q8Ollh?@BzYHA8?z#3@>_Z)fm)z@7I@{pyk*Bw_6Q{32KBGe)6WcZE30q35Og?4CXEB8P}9HRImfjZOa^=%L^Bmc7=}mSH5hd*EDyCQ zj#}3-qH;Ud9###L2`n_7YgtB0-cZs%`fPvM0^JTFmPD@b2v&&6rKYuBHNTjPN;XIB zb!v(-GD8pKpRe#i`RT%r%CBLtqNe|c##$q<$|1P(VHEP>C6QA);r^9Q8`_}$gW}DD zU{zBn90dDkZ*xM%Z*g3TL z@#6;*z%-hJ*+|&XFq3kQ#{Hg=O*;njMzxwQ5Yh zDJ6QyO>X$Sb%+FHeVYpE=J-WLAYV*EHEnE2Ksx!Jn7FX#<*d`Y<>&y;n8TBg-@8(` z539rJpTP}LGEpG%#>M><)ztief=6$HeovRIHSN zkg}#knKaJ$q$FyB@}L`b#r9RX-f3$##YMkJ87i7(BevFicy#_#4TVU=NEO^3*;K+P zQWzah!iZ>3%4C3QCpvB4wJJx3n65MQ&BF{2^9;*~cU{}}Y<(>H{%XK5c$+GiaWx1{ zD(c&6zAYVWKFt~#((R(bR*bxA$;ksXvI~2~$w$hMs$axs2id&ge%kX?4^v;cN8Yid zQM6=^;JSTkQ_#5&pN+b1Aj~dOHiN$8a`Qk_MQu6hCv}rO2vMrrnE~TRBH8%M)#1pEh!a)LBTXIk6wPVX z0%G4RhC3^)SevhjagIVBDV;~!&|gNeZf6nO1q2*Or+5=q?RfXr+RVQ|K&bQ@)pb2y z5s`|3_#da8A6^5QgVnT}*_W};mYS6{y)(z~?)rGJOvgjLRMpYRjYa>HARIirdX;PX zcbm>lp-FZC@Im-9ydC{0`Q?i-q}KY*)HKI~Z7eGIU-Vx?#|^)9VJ9UNkx=wJw_|1B z8v_B`q(!Kdl!}UQK%k_gr0G=QcRH=wpY`=z{eP+cmPh8`8*SB<%%rfQ6-gP~bBi0< zp1PeZOVjiP{!SwNeT8xNLVb0X(jQ%~H4lHjJXL>MICvOJ=&Am-h) z@x{WyJbEtG{sk$Ff+S#5VfyxQljpoDJr+x@6lJBs|F{VfKmiTG5M}2KztyBT4w6^rK|1IX^^qy zQrdsowe7SJkiC9G+QV%o^PY|_@+|zz_RwucSeU(dH2#Q1fsFBZPWRuQzf7(}=3uj< z60-S|H<47$4{`7O>~HoEj7w@ut2FwIO-44@{`El~C%CT<>F#=vXJ(qxx0_AIXe{gN zyZ7(0UcZiPv$nGnS_TOU3bG$**wJT6!RIkX+Vi=)`E`daeE)?;+;VXm zC5leXq+6VlkmSusNZvV);>22Y1bX_Z#3XqoK%oUA&7&0T@F7vNzv)zwkV1N1rr+w( zTo51?=#F($4;Q}GaOxu93!S9%WGWO=gBX~|U?0ESy#8-vD`+`L|>El1^ zkWO#Phl>6eMJWt!!|EmC<=so8AOw$yNrEdTA_CtPiqW;@h=q^8W-|wAl=b%J1JTi&bw>+F%-~{I;R2E* z1~#9Cg@yW%seG1PCL7SAXleV|*g1182!H=>k;h4B^_CNsy$7$o$CVCU~U(>4Z z$;oRn)2p~&0&^=Dr*|B>h+;a;>^=s4-sL<0{oT}dS?=WKP@Hfjd()ZJTa21-2A^`L zBsA#}?!1+$hef=%BbJ%R;Jds6aF3*TicG>)%))cvoH4sG$}rN@?)L073a^4 z8&Gs!{l4TGBx!vbU3?m{R(yq4lUv&)Xk`VVDK30VAs|atg5vPAUjxx;g*8M;&!wDM zbgu!bKlmXSay-dx@Il8a`D0ICXpXm$lifCXw>e)T9$~yc{DF?uP=ii|d!npVRp(!n z)BL67T1~UtYw`RX=6Hj#R|6E+1%=|O+IN%0;u^Dk?NwWXZRKe*gEA4@L znGtl%kCck9{a8XAK#r2!&zQ;lOijp7#>+wEf}dthyd%F;2nc;a{J_A6xcJviD_m+g ze54q-xCucaA^KJG==JClX61|^_hcrOlhgFcYFH4c>G`~G@yv6dysUjf7km?e$HB=- zvLYN5{^wZa_^SG2z3B^T+qz?US{=@ui@_uRM$2?|Dcta@PYzZ^E44N{vblKg-gVaR z+WejxO4KvU)l|UQ*f7LsSbpsIyy%5-^@PZif%!RL+OlWM73xh;^Mo)x&sZGzuw+@& z_dxr%Tw-+{Gabf5>uUHi*^^uQ&MQ5r+LrIu=7Ci1OK+39&Z1XfmsXXzrOs;RJRJ52 z>z_|GrM)n*?7|dAG%GMU#Vx3`DfXjIh%34(31!!syFE*R=YEIM5e1))bd-uSD?b@Q zFIg54?LJ!_7ToCect_Q`<^nQqy%7DI1{xv?1bwl;*Btw;4<9bE9H5>B3){XI1pe2g zQL^Ugug7@GH746hk-Mm>TYJW}5dYFNRl0t*wvOa9-?dJIZxQgmkq;0FC@d~6xSlt| zA|VMbSXQqT@yWan(rbS0x%kV;c@wX{zMrRt9r<^Pehsa_VxDm)|7iQhd?WL>!oK=K z3ar>zI7%vj1s2fku!V`?eK|gc=d3w4_$lC3<@f`OXVi4>?u8P?U*-a<9&Bh>y$@Px z3D#ljdDHKzp42j;7Ru$V$u*n0@+0Z2E5*jEU#)|eGYd!<$gG|S&E0%MtGSIwS^{G8 zoY{BwWg?5IHdvKCnYFWxP35)c%GaX-u0IxSQpH2Xeq+2rs$N|wNY;dmb;2JMRBi5h zj*f~5EO9r#i3o;$fYv28LAr80C$OM%7Ci6jUgFy%c|4_Q&PEYE-lypgB#6%h)WW=D zs@Fl^-z?Agb^7kHps*;QEfd$T?Ffw*NBG|LfQ(#1Al`N?W-x!Kb%SIy82j_z(+vMz zq@IV6GGoIrmx?>0`L_#guF7vpx#5(>*jZj8QAFdH)7_>dJIWv3ky7!kSK(N ze@^d$FrOBA248&eg`)GK*=Y)9YP`_^{JhgPNCn>8pqclOn2p_Uxh+q6!i4hA&w?2twU9*lulMR1`GWkv z7sE&?VZ*?4;CfE41kBt2dat|8rge)>C4=Pu{qcq@%Y$tQ7S^}7k@$ZurUtOiv9YyJvo1g_s6?E9^$MMehGthCPoetEoew5iI^ReKhYWas7KEnfx2<2PesX62rvuN#|~Fk4R1^u!2$tFNCk(!qU>>NjYL z&N{O(E)>4Q9M*Gv)q9f~)%IBL-{-M$a^}*6Ut{=}K~@0D3@$9Bk>xTV2Iv#ls5ko5 zmO45bDAXsbPpv)SxF3I)mlrfN5Xel9xup9(+?}q69>@Rq;a(E4cX_iZcXm)U3Y$fP zY#M>!Cn)H7x!=8&q}Kz!#N}gKq(A|OHr<^Cvt8@yn_|_{oYq!SomrnFxjDBOxEC*8 zFxjucl(jwbXOPgOET3>zgP5gO4Y!_HoWxxvMy!av| zb$c;6s$OG>>vz+YuI$XST}Wgm?VFbCmu&&$KjckaAnfqEI@nqdC0n02%k15ACWTDi4TD;r%`G+E_teyg z@%{fMDYs7DIlPW3+llztI5=2r?woyfTQ{XSH|wAI&WGAS zP02~a=f#n>!_(kdzmqu5)u z26RF~LeDA#0m0XzB4uS&#fm!r)KtREY{sIZ4SO#4&EYzUFJCtHi_7_D3a--r*er6W zmTFJ069$Kbu&n=kWo-N_J~j1=xx{WILs5*9T)Kq5K6&^1ZaR(T9|s54$D_(Bc}0K1 zJp_j~|6=!$*yr3ulkmD6B8g6;QFwh48?_!b+#I zeUD&l4U3FK6$Zt;$_WdpnrG+dHwRUWH&=_t0NS6wqyUKA+%VJipYVBSU~Ra;r_mUGkURMD>1D?v$VG{ES-?U8ow(94PO6n!DY$kI&-epCT;Yg z)?ripXH?V+GV)w)=|^3Xg#bPQd#u2cZq5=(beXk%hyQpZOfZDh>I=%U{CynpnLP=ora8dEkQTapc zTP7-^y6S3qy;c9VHZqPhw6tN@IqaphG_4c&B`#uguo(eVt(uP<-gfK5lClx;zA7jx zl542}K5}9MF#bWPJiR{WHMDRT{4xkOuz*WSa&jL6-p^T!lktQ`SDy5#nP~$>mCnx2 zN_F&mEK6J4p2O2MbNeg{!YguK)A-+7=RP55L<6+ga;A`48-em9HRUze23{XIPIMH&W-=@)YO4eBsZ)|VeYdKm+mM2TBP4>M(O8Kq^B@IS(8T=YUM@N4PVtkf~t)*qp zro$wN{}23NHT=eZmL(>>0reoV1=vZ0V)F9xb^CH)`ngslVJ6VPxiskrAZyVGT1b|r zDr-8@w1qyDBBtY8SIyUNrxrVKaGXsEW?q>N!R3CfaX)?SdN8-nI%U#5pyGYGr6zzz zoi^N)pr$LNs;URV6DUiQo`(e-(;&7#5#$x#QxqgwcQFo+AoK5DO`x;c7 z0mGfw8glBM%nq?iq|?~1@^N?PYI}Mp-EHil&h+g$m|vCGRRvLU`*}fruu?&1vde%a zv#J>?sGzJYVPlhC=Rin8(mhvcl+0uR=L0o$-6P&K+^o=~6S(#-&O=BVoZh>_#l;1i z{%$EZHUIn1pF=0>UFe=ALV+(Gx2JwLH$vMAOVNCRAVvSNYtQ=Mr_<2ILn=-;M5#nN zgZ?;^ABl;&{NhCoie0a?f!4iAW|HM6=RtF0+8$|@?S6H<(u_FUfkTh)`YTam0(Rkq7K z`Er>Z;FMXI+cUYJkY~7WFz4jv`b1A}IDT)TPwS4rtMa}@1{nraUBEblLcMR_qSiqK zW42x50xrbt7Sw$-qlHfEidB$7TcTs7Wol|__x$3P>1aZ2BRF;%B(t87u%Rp`k%A z==(TN4bwj|VtRLR?Va&&@z9k@I{A}?1k$MB6P%v!EiOAdJCwwNC<2dhJw8ToD^bgC zJA9Cl*IcA;&-rXUl-0wo2l~%;lwW_-^)UdU8G6Jv!o6c{+j41YXvABFtDvdbgGe^= zE5C7}0PAO5oUp3u8@VS4fxy#&6TnT4N4$vMhp9E*{17Yf%p2S0(>uM3ck>c85NN93 zr%weP$-SHFJ6j*!ua_-Ws1F;<;^X2v8+J0fsx2l{m^XbiH8q>#t3Q7H;B&QZ&vmmF zm@<^i9JoZn>u{_V%990izQec%Qmn&~$ByIRa2q8R)pphn7AB^Dzj5cGYn@jrM(XyS zGjw~~G&L>FM^_0a7`Uk)ii?ntxa`G0s;D%8^N7!F`R*{YMvLxEXIB@GkDP@CfnCcb zho0{xuGjU!;n?7q>x$sx4drCMjM>KDZ%e*V8)MU7ZgWY4TZSvm9^Ow6+IAJ|r|$Ud zo;aM#&YQ6Cv0#A70uTI|x|qe&1HUBWn_(qDE+zB10KyMp+rhz`)>sPwx7A zypeJBBwJKgRu=lDy0-R^-xHLl%4{^hqM}fwJfbY{j|Dc$HX}I)MeIO5UF7DvLaG z4VZOjZae4QJ9_qm^qgdD>ax<(?w1TJ3Lx~!iG2u(p}LgW zOH}+%!otF#)P-IsSBjdNu4C8xjUxadoCBQV!D?3%^>x+npt;{Tr?xf`7+(on>A!4U znwFbyXBL|;XBGi2yyJVAbs_Qd^9u+JR8X1LQdLnIcS$GnP3{$Zx^<>*VI-jq@5~8~ zxcTG4HQzB%QuF~(qvrFYCLCN`vMYAhE9l_Ne2Ja8{>*HZg|+pIt6}yrHnbL(RR(}) zb-e=jCRe)loLvjM{QY490s?tTyX@K?q6;z~uZ1A;g6+kPD;}0hVJG$zp4V3v-at-Q zGQwtTxnQ|oa$*#`XNtLBZsO%zKaK_^X{3JlncmmS*Sm*s1GQEl9RVaOFk4?b-UOYW!lI>raPVBlYv1};QBnBRlx}*foB|FAih%6p zfzs_yUgR1+RlCz4yVXltKtDK8T5lfhlEsiAK1~D*0HD`G)myd9PlZ zA+x{n^wp`hdj|q4nE-0>^cMpI3O(;rP{UJpfnKSA6WrkBx7L+H<}f(SNpfLP!TjBW zBsCwP@_tYq{4G5ES9M9`fz2Y#$B);03i2ummyj7&zYe&v)*|TMHy54jvb)1`Pfobk zVhV+&zfaNMqoANrQgMW|`+wRe+HYx5j(9`A(L!*%>Db^+nEy@ko3`_MFo3@J5#7Sz zkn8!fMvRnxI~q20+| za8YsbX1tuh79gszsS6R+0zSLxsb4%jc^>Y}zGIRdr%U)Sk;a4qu*&3iw1j?1iizJR zNuALSfJ22ko%K@vH{hwd8Wm%M$v%&ix$I`K!o!xiJd7PF_{j^k?yeYgC&F zz)jj7TevIN&O2xyIdOj)`GoJf-9ngA7N!qH`$J9KjM9)ye58;O1 zt_3nXAX3F07=y^`tMX5)I0F2g;4~amtm&JZBXx8v+wp;FOe{je1%t1h;GIPnX8rFzyn??{#&yA@#Hl0)*(fxd2r6kNYcL*Mp1vyJs4?JrqMr zPk$-k!%p>)`gBT3&hf0jK%eE%%3cPLn6`&Q15-a%#5^4j!H0jWBZ9|EvO9s*1r~%p z6VQ`BK~mG9YYnuV9N*@KKbZ_2zB9 zoEJt+?S`SM95A@`RzM&_ z%eLVgtx`TFAz?rn1TbvCMdlKcdd)K3_-12cF^}4wbQ#+2S%C1=C z@?3D^Ib-WuP6Xq-n)r2{=k$E~B}Csv0W{?lz=kc`UQ4Dek^o9g;)w?EKRAp0H$*P! z?d_i)FABWs_lrJ)O0M?R)ff;%@2-abWw!}{Ly`XP{71{--u0*;<8^DTQ7`&TiN+*F z?ks??g8+ee2VnSv+P=q=P+m!C=@Ww2f1GAzRm*jq#@<>r$~rsaU_4w3$@06r26RqP zP%z-Y^;!DiJ*rO!Gmz;oJ<0ZEaDYc9-IGbSAbwO3`csC8)1`3r-;aN`dAHLp>8G2$ z=YO$dm`#Vw-@bi|goXxK3Ubr(2%y&`8dWm|*L{XHj>9!-tQHf0(Jhn!%zNg?-=d-} z35w$5$gpG+gW)ZG={N)7y zx7Xm{fJUV^0^`#Tu5HsHx^2Uj>h{P(_GHkDVzu(Cy%sB{M}!@xVRkjIKn$iowKC~E z!C;wRr44VhmKV0RW{_<^j%61VQP=n+J_A$F%P$l_L#U`=%E`$AjTCg6^ugKVtzHo962}o9$PhX| z;kbgrl0)wy=+Rj}J%An@E>|VtrlXePfYpvR1Gg#$Lo4+3&7xj8w_*nx{Z zHFZz%DJha|z7O71T)ZT*neF*XjU#>EDY-AFB-0<=m*y(pQB%8_)2OMaDypfu^);3h z7n_0n>~XuLRu2m1K&zga4_r?zZhLR0vtp9s>$=nH>zz0$hW*&^n&Cetg3w>h(^~MQ zlGt*mWxV5$G5Hq13JO@Acjb>8K7<2_1Rl_E2%CIgU!Q)vKP(B{fLMg@#?|5x$f%iy zcNYZ4X3&v>u;}P*vZotfkU)_r#UtBCM@O}yM`q^D(}L_+Qb4yHkg~Q|C+st;+Sr>31>(}nj(YZPvhFA+G3gl8pJWW!~pSE$tUF$FpAnvaaf6#`$p!gFFX zGJM-B_d#)fP;juTyPQ-+v54lx2tZtCH%4+5W}~E;6rXLr8Ua=Zauhr^Rpjy6rA68& z0sA$7cv~fT`A<$xRdvye+o#P>kD;Y#rz05$AHmZug~gz1A22MQU!>?LC|fap&G8d| z6l-mkx8sGZAMP%#=jyT2rU&@1mt;&$Lq>h!!C5>T_{eowt)u~<5MbKxd3n?B@0S5> zy?HTXBP6ldCPRe{czg53gjCSyASWQ*W*2<`8N9^{h37&wFYl7-{rfF{7jTP=>1wO> zN^|!Pp_;0yyn+VkhS5$cAN~%C5j8Y?d_1ieii?YLLHj{h%@+xYcdV?N{!kfEUbj$P zrxnPCz~s0W5=!tp^8D<~N6{+qJLYpPygBU+MwDiXT~4K{_i0H=O1P!tp#?aN`1l`D zaRk^!JTC}rx>7+?WD|)$S9;Xc^vvBm#c3B3LBBS1G>5Cjp4HVS|M;oJzrDRK>UG)l zxMf}Ie^*yC!^6YTVO4Bo(2hslpR%B~tMcrRWc!v@KL`(5&%5_2#{A%*<$U@%G+uuC5b2K}Gx`1R?p{7Oo}!?LJevaSu`s`$C->wUF9jO!XHVY|3oBBB;GYK4k3 zIjya`f!%X^o_S0Sw4n0lvk!Vc?1OHCq3R#I=0qt37@}|Lxx}N0`+*831#pfR3bJ~w zGeM^B9f8$M$)SDbF$n6Qr4CeAN$BX%%M1^bY>V7Np=8wxWi*&m{SV`FbGtH5-gJfg zUw2-xb8=3u**n+we07DwmP50veXh`_jOLc1J?qY6Yxe)Kyf@TD;9?n&(b1_Wn7_)T zad_xiX|;F*Edkg%zqFu(^R4O2URTZ%5CRfx8R>%#wVoNaO^c_FYLkJ5%x=SO!%BRD z?0ep;1yB-gxM>)rGKujTtJ;p!gO4;WF0PTi0ct+Rwrs|w(6Ivdcr~^vDG|04fWojA zfHFeK$JdN=z2TgO_;m*AboaL%h#J@($#~ug6DUP%?EjwYA%B&4K;qjW`fzqN1Wei}c`DPJMkc zVCfvTOQIuJ#9&7CV&+n@Rg3ewxUEvfEiFGVKI|ZXvJ!6hwrD&4S`!w?&=Q&N8l#^_ zY-fP*F%z+v0${vpnPiW?zAM(!D$~>8syY z)Z~oO8)D*r@wcxcrw5CRt<22}1@&VKAUq}}Cgz8#9Cm~6b|#%>Bok_6)&yhk8t-!B zBtNzb`p1r8Uah*Ah;<89RqZv(IypIovS^j6g09wr%$8rj-jVukD8#+S#&9en5%dD} zDIh&l`W`)_&GEOe5}c8Q>~V8*>d45*-fu9p8ywj)eXh8Hd}WjrV)9I@Nivv-tipp> zeD5Vgz5T#Zfy|Z{8cNQP4H3kivD0+~RT`hkO;9gB!a>UiaMZ)0QA z`}glt{2m~*;O!c9w)A>_4`|)p-7DvA4cgvkrv*QmY>B~^JHq0%1^z~8e;a8`TJmnP zEyf^k?4Li5x{sqDao*tEy}$%kVth~L-@Sr#aYMzo4}b1Jb~tX~349f_7R%@HT1GQFB zNr`~dw#1E@i3u)tdz&oTnsneC7TY#eF-)Pf1N}cKy|E7*-cXszyOn zQ4yU+)J^dsgWn67 z8_PmOMT2T3@2I`>IDj+S-?!TZ$a2hl&FT5M5hzW6UiR}5-q5e`h^K3TewQ;YNhVBG0XQ@{gOu88co3M*-+llojhtQsC)^RQm?lkhsFDR53E<~)z+3SkBi}QA#eJir*Lw&6S8g2pJt59>b3dywfVAt^Xts#t z4-5?4dR|%Q%Xr1Kqz^rSR@e0&BD>uQQAIHYnve9G;7p{gW$8Z)JIi_iHy^iL3q0qi z9sb5LBMemIrFtJBpr8xBMBN>g6Qu93=E#uX@B0J>bk#S zOcVq}DM=AfPyy)%0YOnhq#FT2y1NvVP7!GiCEeXfOUIFhLw9$>Gtc#Z>%%j~`+T^@ zaNL9YoU`{{d#*Wuwf==rdrHX27=XqAg^>c|kKv}Y!BPly%5Pg2ky=BfKBdD{4-nE0 ztGyQ#qS<3&i_8?kjf$7E7Ryk>&=CQD}%%>j{6De3fDJUrb!ian$vngt{ zQae?ilJbpM%fUBCt&F0;xUx=8S}@9fGHL;bD8L)HtD4L8rY9zj&e|}G;Mn5g=0co3 zDlsFYZA^E7Qzlbs)o)Ko1WYKB51vh;%k-VaU&tWOCuE1%Z z6fr4)$8Ul7Pa5Z@K#Q@QiYCPXFy8)46WsUDETB3w^JfH^=%?DVnP;qQY|vD_qU9p4AdG;Zm--g8HI zIXIu69?s5%JvmCTQ6Ts}I;x6tS~Yc3FAw=B>fN8E#H^#Mo1T+HBP4`?X9Lb+eT3NI zpQSjyj6fiIM7+U94+9}g>vqyY=j8eACKUjlZ$aOoKrYmu-jAY?up+CX(pG9Z291(ownkOA^W;(`SVqXx;+?OvrrR#?+WO+|`cwh-cERf&81&M9}71pd_KOy1bQY~{V!##Z+ zKs4Sve87a+t_~L1t*atVCv*xzVq+ygew-G5l1Y{O1;gE}Zv7CZ#;7&Z5kccx_!0Ac zuPN!+*$KoH(z9|_EpjV!*`QJ~RsXSngHkb&o?rYV#0FrJU|ya^RnXcR=9@Qf9zOho zfD#48+J*8d^nBm~0%`*1!Gq=3XR+VWAw|ne0V+p<*jQLIdG6=f_LHu})6*XT{ha)n zlJnIoGc!`MW%88kn*dt;5Eo;1LII%0=ZKuSo4{v=$iEWKiSzk_xDkyaG zU8<)_@+3E2z?gJ|L~MKn;8id~T|uFF+9D(_4)h*m=$+-ZAiGqf38(dh?U2>}fD*+n zL3Nz$==tGiqpU_in{BfjZ(@#&jF=4Pe}!%Y38RQaix4R+*1Z@|g;-fq&y{J+Y3}X)hhOb!jM%3flVfl)bn0 z$lU2kA|KeYWfc|0JKSpAfH@!~ZQpcve+CX~RqykImGA}yB@N#kj&oCI(|I#K;LRVf zSY40MbZV1by>ca4X`$Bn^3~h7elj6QyWH^b@ShRk_S{Ort)kF1|=uPz@w~tth)g6B-Y2uirmg`?(FRFUZ={;v>N#oVTF|9y4WFTfoAm;UKqyQiyN50 zn#7gu_P7hSm0Qk&RrDo9*&q&#*@|%#j838Rdm!k-9n8Oj2OC+f8S@WlpTRnFw0rH4 z&1Comf!KB`!^EWnylKyJ%VRM&>gq&S{&B*j=(tf~P;vCb|Ms0bdpzC;ILL$3Kme+` z`OFvLzs+(YFI-Bg#@X$ov%Y;#p}0YIoZ!|&*Sb01Qq)G>`s&7)G5ZhtUC5GIJd$1n zv8F>1P_%b1?G792S6)Ck3*TnBC*P%oLE1>1OZ#{iL6KjQ#Y&0<|KWHY__vB6a*B89 zyy8-T^zPk1AmeKGXBKRoYfGcr;s#Gzw?8v~kMEOs;Iel{UaRqp=9-QfyB3}7%!LJ8 z?{?L#vRi-vD=W9s1OB@wBvdE8X&T(>i3@{e?}oN(4=pwd zih2b7fnoh+Ioq;fpL^;=Iflr}%miV?S0J6WVr^z_$88jWtufwlK}+3rU*Y-2tR=j(&#yvzZrvl`U7*`qsK2fXzO`Q^#)BaE`fOs9Qjt+% zoBj<)NXQ+C3=(b&47oJvPyf>KDZJusyi=Fa+rFmrxdJLi(35JRE4eiFrgx(+>;mv| z?(N-Ss+fBu&9^QObS7?P9jZpoac289&*EB9Bp9a@bJf9E1rwVTfc294_b=QoUWtpp z(C;Ma7C0n_WR{@j{%xWiysanT2nN0%A%=YP2$%dbgP#b{;Xo~Y+>wqQd?U3!FrR36 z%2p=GVBRxrGqvr&%g>=m@O`*k88jV>T{@|&yakq)8g31-zaZ61MzCAOAg&E8d%9gK z9RDSV>HSL_F=^@P5eGIObT-q~yWJm`@;`d3zv`a0u!)=n72{)M;_9c)r9N-eGMX_5x0#*jMa=5;jBo^_DTtyFFwn@qkeQ)L7x-hB#+^%`a< zZhd{S8kM!MWM_PzgpBfqqopO?vF2@UZS~7G{g9uAJDjZuPj_jID+~$ibq*;*!^wG& za4Plo?cB8YWy?pik7CjK7H}ZGaQa7Y!f71Q z&WJ|p%n8mvXQTFoGMnd<3X^^m7Jh(Oo%Hl<$jxb>dF1040L^yv-@k86IMpH!k2i{! z&-UxREh^MePZEJKsorVth;^%EIsP}h4TzbT+l85*pFarDXqcFSz!8sY+$R8nEl_yC zI4UnMKV`wawF~N4)*r=!hTpz>QQG_sZ=GGX`Mo5Xmp#o9EObE& zTr}0@B>(}cs^-w&UIX4>Ip5XV8r!U_W3i-^aJI_X1-?K+B16_YJe-?C`t1DNC%`X7 zMwJSH@=AV__4+!&rq#Tf=?P6Fdp{d%+`#?@LpM~sDm;|Utb^G74pGzWhJFnXnLT= z*MsAbnE3VEw}m~V&p{5ju&{u;xxc%+n^BgUnu>e)yIF^4%8A#Tcc`J^(Pl*{4kHK- z=m1Rr;{TR%g%pan8~}GP<$|_&`0NaVU>-6u25xQTH{$+9Z;`XYqN1LAU&tjC{l7y4 zWu6)nufrPU4U2>$QwR5bZHAghHx}Rxy;qh!^fC4_pTQSsmRffrCi^YAA`sk9?+I z&yn-ZZf?q`s%}an@u+gArnF)Eboy(}C*P>9Fwj`ZjAxhYdXmzY))T764f6~LqhGtY zj5AM0p-1xA9~iIB^l_j@0jNMRl2NumUmZ6*X7 z!5G>J#3Q6{S)UlDQEFZJ+yDD)@pnWG_TvqN0z4mIq>NA(pOP3_vpF;LO7%tERaS|` zEl@C9jBUF2EcGS36w(9jI%2~4A)PWiPqe+bP9DkJd|goS=IXG_(sY%lyZ#i-Svp z48INJy|y;SwVQY5R*Z9X+8=}kci1ZZr}eEADb%J|{Kc$QucXywWEw(>*9zo@y4&$Q zT1+_Dd^+&i%<$u8d<~2hq0r8+tOT&9u@tbNXB70iwED=&6|_-A&y~Lbjw$p9QLaoQ zU;%QvZzAtU~7QER8bJK>$U?GT(HV9C|5^D#$Xu=thqw$WWGZi>#scJ z?;Z0i+tyHi^!jApsgyV|nw2f4Q>2xbe~XP(A2&P1AJLMNLuW?y4gdQmr(ioA9iui} z%ILkb9By*yogI5B&f8Il(n>9&qpSb7 z5KzUdt~r1)5No=8Pm6a8g0c1Rji>45j?j7MF8^hN`*7hWGRmGl?3cBt7nt$R7eegp z+(CaYAnIYOtWHSUY$GT>Gq+I@<~g}brywXZwuYzZXdP_knzsP5tqz&b^|i)75_0b; z5Da8eE3m=|=0A~vVIlaaw#>BS#a zuy8F4jAP}~Cs`HSUdFIPuJ-K!F;@7o@$p9WtYU8E4yy_0sx05H)MW>D95GqABtY=X zj~7-Yq~ES&tnj+{d9*@&1)eM7i>y-gf-uqsv;k9ItGSN_sW+4nViEhB5zT>I>Y@$~ z!}ybj9-!gUUU?_CUt zQW~k`h@lp}nPf-`X#(n|=xx2++=kwBq>?daZ;#z>qcb@n!DmmfrmeFR z2DCaP>*k2upM@?pOKyskLkXHJEiK8+_8-4H=oy$ktqz)=RlCq-)Ht!Y*FOSD6wC1%m=Q1z&yhzLw9(=lyE|-D2_C&$AfKS-`O2>vJnDv0k|Q zK>dUBzRuRvR|wI`cj|J=R>W@f!hI}=D!ZblpwLx)j14w-NUU>7dPK84DdZ0otJHi_ z_+mqV_6gmO&?xE8KC<@kQb6LE1js1RnPHugp@5P(9%ihfV*w##O5~lt|Hb*~k5ClY zGW67C60)mf6|x4WEO;OAe}2foAc0CqsjT#piYWu$Owz@L{2J!oljqL~B04}5ZtN2r zoLpXhy{w~as{R^CZs@U!i_4V@tOzq|K0s&m$iSJYPd4-p73V*)gvtpsNIz*z?NUSI zy;})afagUM9Gj>bUiDT;^y4%|i70&H$jD&Wqv{R*^92L$9w;VgXxQR_qlLFsMn{An zZooF`KFE>Q#=~2G<&3~R#J^AB+WRLfJ3F>Zt@MM6O7qsVXy4e4VqWdi$iQ~f8naa{ zla%7(*ngS~cS!#sK$?P~ZpKx$YY*{KaM6;N7bSVrbGVx2w?!XSpP$bvo>&KGZ_+qjUp#h%VUrcJ!h7u1znH~}q zwNbOF&P0rUvZn_WO)cWcIxQmTjpH=2q1Ds8FoPF;7KC7mijRG7Zv*dSDRJA`Zj+9O z&Ubyx9wLf_6(=S8Pxe}R^sm9#3Tpqh6P%b^SXfE!b+xo5nw~p5SWupNvy`x4d4fP* z5>br0Lq1HERie8!X_wQ{{uES#QD3i}-XC_#nK0u5p0}-g1fYiBT8dit?#+OdFEi7q z(7F`KoRxQYsHx(}!)L$1V)4=)SlW>qXFSVoOW32Mgs+`~?nctmk^z8gP%Vaq_IX2^ zqe;%Z5y%{N7jouIdgve+%uU4=PuUwb=pZe$QUxSyARm1obGG{cu)fH%!R;-W`SC&D z@JT3dsyRZF?%_iWAt5qkYE93l2#uy@;y4-jj!UNn$9RXeO%(HthT}2+>6yF3Nycl- zl5&TY98JX{~}5rnr^h{ zLwx$RWV8({P}yKMVW59LvpQmZG^TYaDI!u`mJQ~)Y>l#bknRDXD+XqzP}$Jxv-VXz z1M?@;)Il>dAD|vuZXz}w@$&k8_y4tZOkDK0^UZ4{2dr(p_0fmMLph7b#G26C-vOHe zgcB~bkmY__`H2J-CoIQ{)5)@4n?MM95ImaFz1VR-WOdkj2?{Lqy`vvidt;)0r)u}G zH_x5u@M4+ixjq|n?a|ZFQ2g?7CSbRP9UMq{&Q8THwzst2yp^0AG0=L%!f?grvt`BK zu8FpP3~839>t$1$iy(-5J_UE^G3%qN{sZl0lij~TBZ`!X`$vt@)-_R&-Xg^f?th&A zSbcaQ`8b~S>zLamUwivAhZ#iAiv)L~E=ERB4TmD?Vmg?rd|ZQ!v&FiMj120>ywS(lM1AAq0d;jkewq2EYMhs@>x{s)B7ChX z9MPBfjQI(0^~`l=(V+FSTBIn`DN>2rAK>adnxfIsN&0MJl3LK$7FRaeF7YHVIkPev zP>raZu|jy_X(`1&Q<|<*mS0lZ&Cl<4SFs(nHsbw2J;C{MLx@RdaLC$Wrl3wv`F#^y z9d20LEA5vUY$!yV?T~sKP|d=!vMe&^t33TqX&+!9`%SC`8QdJqHD_8Vr&$ngw=HZx zi3gG_kvldJ-XXB$G=o5{{FncqOavYESKf4%aRfRK21@pN+=9ZDG* z7uOL#aRYM8@@+<}Ae2x{K>^Ayn|^20yj|bF;ij=o$;E>)^N;Nh(!fWGaJ?X86FLd_ z_RRtQ3b2;b%^H7jxd{%5AlU^kXN>jT_82S+SipQaYhp6jJ!bw1@O=OWqsmb6(jp?{ z(%$#OM&`}~v}^}HLAcDX=jTzEXNw$Aae>QQ-gKREb%*K~@3!Cw6lJe1Xs{BCQv$}! zs(h_`c#u!t*1lC5{A)PyZJ&)y9`P{v-fZQJ zzZlwGiuRf?gZCPb+j>RMto*`AUSU2=rVj9gx`&{esI26Lw-SIoXI>l>OrPX+&^&sD z(s*08w>{b3d6stttFCHW6*5tP8(a)mLTL~8Qjc4l55Tlp8?{S8<#NOVaweEbA!e>u z4M8co8iEr{rZEJrO7BMDw^po5TDA{_h3zJ9o}Y6k!2$#+t7CI5+V~ z|Kv}Lx^VW4#CO0y%2)i}0b~dxECL;f%7gydClj_{Ib?M?x(mFcz4CMY+}af@EG$2W zmVC!EKer8H9)oPHx==tLV<7Vx3`*NQX%}4qhXz(X6ZQ#)lq)MMN3$AbVd3Gvxy!}u zWp_zP{R)3tXOXMYfpFQ#8X#_n`MD?+EMavzL7u(_9CjAjjoR)wjV z)qaJIViy$<1sb9-Z^~*!aj`svL8G;J)1G)j3&HjiL8*oyeu{P8h`RHDA5A48oHX*a z@j=wae-c~=bNumM5%Wj|TQJyWXfQIe=T260XXfNuDO-Z#sw}G1@@ROWUcgG+I7e!L z=M|Iyh+U{nmZ%DY`viKR2rzi@u|Y!Jd7qFzvjI@&wsw0(d*0_p(0zfDp<>$0_!b`a zJj??-lS8PPSnE_B&L~4zN!+xNV`eqC!$z7QAafCHzLpy!2B39pJ*9H#JYJ0Jy0M7x z1j;eTKDEebUzK2h3j^)Vg{KFxDh_LXb@)`o9Vh!SfSIRBEQbg5tLB-NVM1zg{>S@( zoz2x>Rs{pso^P~Is^1kYs^Lm8;B4ezKY9m*$}ZrCl2m78Ld6{jYW2i7LSJ;W(=npA zIJYx(Iq@1HXh=XrxIHIy2~S#R2IyyTS#4x0BlNV%6&2qHvW9*{MFqKk5dzngBSaor z1HY(KGF#b2dxu_2Y|yBK_Wby;h#3mix}4JEFyEpcUQh56hQ?HdRb4t@ zLdya_P*zP1erip{UO#ppKdFq+F29z`F~@8x;w3a|wP#kGT(1WC{xOOicPOFP~p*eiZ__ zd~4Zc*QXO_DpsAN_t3ha|18$3-2rI8dp1(&6|gnPhiqGm!Sxe?c|$HOwtGH8BLF1R z8SVl-Yv#4HbGM&3dfxbgL&jAgV2>GmHcNdIcd50fLy6UX!vzM8jPz{1mQyOrw&q3a zg@0|uCQCWX6NACUN+F$shZSiZM!d>0$q#y`RNUGi-LDCFNi+e;ldBbzdkWm z%oH*xjKmr?f^eJv{UeZ$v%$iE4&sBX3R-U_rl)ruqAg>2mk9vY9xk`s751uGbDjWw zp{4F?w0SzmmPCxKW`8ja!Rh<+CoO2((SHo8ui;8YvaaOg0D(Gm;{#c>fUrMe7D9jv zHMU#)>9ef-v1=?*Qoz8Qa6Ze-y!W30?##468FCE;lErZinzj-l{COFf#s#m{Q|Q&3 zmQAix^!GclS-eDxy>)$juhNL)qV+T(A=<2LNFWbBPGRVli`ThD0Sh3jF+Y|7@`5ja zFbEXrdMR)HsoW4Gqba*i|zD+Lmy-z_x#h_0Q)o={obYHNFfXaYLxer0NCL>W|naAfQaCXVPV6ieI(cML86 z^8iRdf9dLa4YXh|8hC=^*+5b-941*19CDv_GQgK0a|Ld#b?k5&&Rl2hKLv3%&-6Ez z01}=%rgh&iD$Vw88b!Cj%m^4c9gRR7=B1U8Y-lJn-``(W#W5JfN`HQHOI9{?+y%L> zZa<+w@7J)xu?iu+pS^G?B;SX`L1wySv;!-&hpm((e|@GKm1|w-m?Hc=8bqL(vEKO> zmY$nQ4HKV)gzwzq=0|5UCZJnkED8&&p&)?nSa)q$7UpXx;=O&YSd^rs4Tfp)A|P2t z-&|4-kHQ_RXi~C~d`mXh{tr=$;|^`M;ydq?Le(4q^+Bb1FzBp^v;jvX z_jXeOzbiVQ z3M(CchZU4|IZat%$Z$-6n-#@t%L*(9+(+w^<*-{gw?9I@rd8=?LQ=H{z?v``HFw8V z!N99Kl*`}#wnLWS7H*-lXJC5z-@$~eR8gWlaIf1?EOecQuEnYsp6KuVIkyMCLP}bi zNipktQFkm{>MgkAl5wmLyc^rPDXQKNc=mYrae(~ojd(DrFx(0BzyNct<6^9shzmK$ zs$<f10E^zFAjY{1@7#o0T6T_Q;;Nvq$2RlCDR4Ih6rXp>a3NiFDgDjnJNSvc zA@84NWq!6i)}K8$t>!M ziqaMqFG}r(XnQH&z`stlz5z_LVzlZLgi>1cOS+3sDeb7@c&g7|nTl>I+}u~CTuvv{ zFAlbJ(QBw>4#|BO%LZf!9Y6oSC!t_$egW$anj3%{!SA(feFmAd@OcnC5bMyO9DjW1 zPe2%v4`u4=wW}n(byR5J1~!8JPtTOg+-NGz?kKEG@C`)9^s7=~zD-EDM&#d|R@P~T z_i8A>xwbIV4;CpxyhzsFt@f*A)%k<6>_aUmx|{qYU-I+s5-21>82+d#&E2T|{W27y zN}b8+cWT1Ay#VY>Tf0Taw51?``ZLSoL}{z>1OoajdTSAA+i`;k!?S0tMkOC6r?eq^ zSEW`bysRt{GRN69M3FW?@D(T@?vua?LxGdnr{RzSedTOANG)m?eJ2iE(P_0XIr5xl zGeA>$A9huwGhjY`{iBVAmj>lUyzD?Qs&&8^21bDUECr`1gA&!Y&KFK{e(&C9~OMfENjDFJ?cdk0z2$b#3iskJ%j>C?=l_iAdZ z=m-WtiE^b|XYCV67~K8+{2q+*j;9o4__k-h1+`jWm(eu}`GKPu1n2p+A7KHBeAM;A zXXO@<-olZiQ7)R+%0X(VkKP*?7@EO%Ot$jZUJ4bnFk zPVDIc6c*G<51+IDbH?Gt)(5c!#7u8D6GM=x!3P-VpqJi3L&!Vs@d6g+KXs}P!+L7< z%!RMGyG_Z!FGsh8aqi_q+zTIRxs8YyK;?}t_k`i$kY0lEUPD3D&``zOisQe0l+$#- z1|iCeEc+h^=k9Jxf5K5GGGbqk%9Wv^vgD0L&0Q(sDETKpWb^l*-fj^-q9N(g$({ra zSXGjebRc5kX8HH;a1*?z7Y%4=XyTS4>4}&%+W{OVVbdpoYBlV+1|f5n%gw|ubedkk zJpkrVK||EUgdJ|)L^b6pj2`#y&w%chMoOw{cwOJTf*kkt;$eW>R8QZ9yaGC9b}r@B zg~O03%6WL&rXOS^yV_R*zI0Y(h60Y1r0T9HukO*N_vrcapZ)!4<~BVOH54=aZx{+X z9hAPN=H`3&lr2(Rny)V}T(=$A)y;?r2^IFwN-*RFyaEC;#wqb|y&DbO8JU#Q5Wj)V193G2Jw3#R zXIg4XNv(}X0eeTjZCOA|+ho0oxOfSSCoiwG0S(NY-^x(4fK$0q=-nE`M-z@BI#;RO zPk88ZWR_v_6OcAYW(j~7?XxmHHZfCbb+rdTpl*?6Uw-|{WjspLe(u1vSXHq$T;UI5 zw%X$bJ|LSxDyC(8O#+hut_m7~Z{NHCE%a+#)*D#iC|6adN0V75rSDZ$7feakCW%P+ z^a;Q^pxf4u$jHjlg*alD%SL|jAac~`>Mk}-;I+O$)OVOwAqnECVz#=JhK9iEp!rl9 z;uk9%7Xid~ny@w*kQ;yj&4(*Zz7(bw5D0|8SBQMUbk!Q$roh30RDb*j_(3BkGmw;! zk(s-pKZ=r$k6h&ivfn4jPp>5)3beAG6nyq9vz}2P=dv9Jav3lK7-?Yx2Tta>$_ZOw zpoZE~u9i^pG|%cP8vc`0|IkjXgp|707d=|=`aF-nL@`7PV_8lIV}UyI@}Ts z8GMq!Wx{7eTf?(5U?+MbBEm0v6Q{wX6f#->sZRUd+^keo3%cuQxVxGz%L*tbW8TVC zHt5uVJ#5<{%!14u-9|5|PObb9&@sW0IY0&LFNM^1MzKYBc!|r??aP1w?8y#hLeeVS;`jE1+8H~fBy$y-S1;z`yV+TzpafwU%#^X0NmPJ+ccW?gtUreVX4u2ErpI;b00Oc{a#Y#63GlJ5xwPtTsgE)V9k`Hs9=YIu)|i&3w{#X#z(TxsHH zd{?y-Ee|uD1{Q{#Jcr%+PCnx#b)MfwZeZU*`>SysPfL(vc z040Zq*GQ*yogd3{wV_v9m>?Jp8*;!Qd>KtbL$vInoS`gf91h+iaDB!oJ}!z26MmH*8SN zVv-Q&YJX5Lr`d@uO8X!$--Nt$qIA~AfYh0rIOKomh!L9tgBPb}xt`4lY5-E5a`UxiTT7{c{-KYrYUNkFuoK0Y(r zr6+9AIF00#<-^X#rS?CTWYGETb*vccxs2w;k^QI2lA7P=LMOY1Bhb<|~!bMn=qL)zcs`cSq2@b89hS!Kb20!0M=5Hq*Iq@7_n#t?a>=aOTLuq{bw5xE+ulxm5>mQO@p<;i!j}*@L^E@#YT3 z%noiQgX}M1^=wj$vSs}M67ZTzl(dA!y+X}S2Gz{w>hfxuU?t)pN5ROB5`>y>KHXa=vPgKa=S%d!= z4d*u!5+Vu;{FJ&~U-R?dZ+=^8Z+{Dda*6c;?3gaURLC9Kpk-%IX&knI(wM7pya!QZ zd|s$&3t%)g;SZtQq$#~EESvtI{^6~whQRW&A*M^T8f*?gj6(T^S59I*9GZwEI6n#` zcpv1P9f#Z@c_IyXPC-|vWO^*%Z0VTYxgc;2*t*1&lmJMshfWwhIdgKhRF%gyF4GE` zbJaM}=@yOrh5wbB1y!Xl0dE5jPen>9>3+WMB2(vMHp(u~0iEV`%iSr_V3K9MH0c8# z%(AhG%8hr_)A-XV2X7Pe^L^&#UOgvz2I79O@eqH_ISc@iPHQ(mYd;dGcIcpr?9H#h z$rp6cE5Q21zRBj6hPPg4@3CAleZ;bQuFrxMc3hxraOQd57y z#tZHWd={Yz`2qJ#=KBGhLpyOs`WtxBfVnpOmZiXDx8DL@g4_B38WHpOey}eh^ z$C3bN6)f&tU{Y;ITvid6je)9}L&Enq5FL(e!eBqMw7d-u^mjqq8_TgQB~}2yfJMdf z_j>~?%H2IV8(?+xmp#d`_ij0>HP(1%Bd_OeTw`xe{r&s*$WHwaKf-)`N6Ju=prxze zvwN@lLEJ`?09^E-PYD6i5~LXryMkVZi;<9czJ{vtKJmHSaR)Ku2$%vvV-!wy{+-MyI@DenlQT)eE zjZgr1;S*d?2hn3f>te?W#@Iw8(@eZZ!ti)H~0VU zxB?M((=Drc)U_ITUzZ%OofudSX_T=TbRV8=HD1kDe1<+z0?=+(^&38p8GkbS z3$V#;<40Hi=Dh@S-njdw2I}LjnW1Meg$SW+`Eb3jNg?@|co~9jD=0i*bD{;bFb#ac zFg0C|7*xybDmZ3z+> z9DTH|9TVM0i@WZM{C^Mc{Xftv{U=RD|F02S#Llb_4np%9%dt1c^t&3?tso)2 zc_!d&Z7b;9*cDvFk@++r!LziqMkf}r(8n{srp0-$Hc`=F%qT84kXLQ+v&m$-oLo&_ zt0;1mqAbU(PZ(3-#=v!bw(FzR9HJk$l*1EeRHsJ&NHjgsmZtUM zcu$TG=OwhyM{B!zw~8^}{ulYZ)~d(biJ_O=q9#*eXtaUybR4+udZNOM^HQ-Jcybml z9yOc2!Sa@W62={R9=+*}#&bgoE7_{)+g+HXBV-&anf1vJ0#8ew-b=M0L@lclQ%MJz zhE&SN8zYeXcgPd178IV+JdWHdqf*4@`%T|!U>?5Y2vUQusfJ*oXe-_fTRU)gk@58H z>JtehJK7V0Qu@slfsNtcJmU)I*+S|h91T-8hVv!!so<#@5~RogB`YX^7Ch6%2(3@W z{nxm5MOGe8T3%a5o#NS4(^>OZZzXs)Oz^GRZ&fZe!9Wq`LDI_=$uc}7JfX<)I5TffK`vo9>HjHbS2L>|?_W`g6 z&|@A6_{36gJscS10YrDg#t@athYu}yyF?*zMRAdlpHK8tlK&=1z{vD1Y9HuIa;X;lW^qc^mR8X2i zg{{4C!ks4!hb0)g~E=h>2+e)k!Hg2HV)?A?nb zaEHN$sd>_q)p>G4rlXq#K?!K;J~K0`riKx!Lz8*<7>=sF6Rl1D4fkj1aT4!-WO~dKx$qHy-vS@P|I& zFyaRAZx`6xidDQigPrBHt1t51h2mW=gktShUJ)pWDM-b-ozQmkI6uX3J|Myn$3Tnm z2D3GKXz(t>Ws|)iEp}5nre4ilCgN)t8im+Z@0v1NJ~`kOS7Zn~9(H-n$<=_#?5JEN zzqvD1oGbeB?sYG6kp4s%S09DMbQ$9cX0LvhRY=eKdZ@WKW#nbN!7)43m8fefNt8cY zN$F0Hg}S{zQzqv0vhpD}x6hUHqTymgHUNRq+0Q%%GGgF1Bp@iXUStvx5%mf7*HyE_ zg;P53koQiWUR+!&yoHSo0UemV1t9C+?^?1T2S0Fk8yyQve5a&tVMP*s069-=zMTNw ze-A`Kj)tAfb{2>C`c2WQUWIh?q`!T z`eAi-ZEcfL3c5AB4qy2?qO+%H`&ur{(qwxK9OlI7nQuYt3$nTbkbsr7Hl~L*E{FeU zh%Q+jDK}(`daw)xJH#o#IB^g)l=$>12#Qh{^m_vnM3(iYQiH(J&PG(!Y)~GK{)2HQ zbn#al7+7I1v)Pnzw}|w}@V<6e;k!p1FIC+}{qmZoqb(qWMpSTJNxex1r7MqXHS=rPfRDtGo=MxGL!oGN{GLU zHA7yhGlcm~N`W!wKZWmhOsZpob_6QN_N6@G+Ut*9ex4r^a#a_1%lOwubM2j3%QHnR zw!V7)fE|&EtdRj{tlN$}6z1O&cMA*vxoIIC(pr6HZ_?vG+j54^OJn6_3jE4R* zvpTnNv2;dyo}pz$3WQ*x{2LVKNI*cu@_7magO#-(j)z`^!Sxepd5;XYKWM@b1LF2j zAe(cSA%5gk29dk^IU0BAPG@kP00zbv42f65@|64`po-T#-y}mHVYXR^Xnlfk`|1JB z3Ht7*mavPWe&xCkE%OeaJ*3Sx=*V!K3V$6CAn(09kRAPyy?S>^bm-%O4IXx_V6~># z!D%0Ra(48>>84hH?0YFVZ)`EC%dV4<>QLgH3#GgdQ&(R+Ih!!qNE+)csN+KEas zQ^T)1-)A-m4z;OqEo;o!dZ@Urd#pYFNpu`{xEc<}vqteiBwfuSU#omL(5e`Z9 zCwDX**BKwJ*+HI8A%tGfZb?_b~|;WaPGvnM|W6wu(!%IYnOud}Dl6jq=G3-Exp0XZoBiXK3^}-gNC= zP$GT)9@}^7`vjE-#*?@@Gjw-uj*fRpD@+exAq;-TKlr*_@J*&eHcG!fGuOUS8rr?-4;T z$7HZ#mS|Ru->A+F;k(zTSxaSP!#(*36y0;*J4lNCK3~ksv^WeSjXy3r@OW0E)mhtxRp<2lrKrZ(wUBg@quDXu z6I@~n`&>-vG`Zq0#omg}7us9a*Mf^C$>3Ne)N0{#UDl|JMYnw?f$ePr%3zYfmS?Uf z9`GEtN-R0kO;mHI_KVGB>x=LBE*)jr2h5EGJIW}Pe`0*~Bp$JFu13hoZ0oE* z*?n)@_uhVqX-ui|kAzyH((DgQu3;_;fxh@makuEuH&VDfNuE%#M{c-8!cggk+~CX0 zP%8(^sw|&O5qc8Mfr-y|?C-NhHH0r~-_^VQNLOvB_L`t_)x%dBrl(b6ZL9&2&v!c_ zO7=*twx?#*mnrDtFOq+4UTVHGss1(^H2nI&Tg)4|7jm-< z8+G>Xv@)52dbn0`y0n#@MACFjJo)?AOQbDe(%mv+TFX@o6f%C=Hl+1*o#y|M1_U^Am7i;e= zU`yhy9eT9>)(JE@FJtT=GG!W?RjDd5dBS_mk@?k|?1$dQb3oh2CEDpHTZiigYJji(^E|0FTWhb1!L)^{@o+l8&LcD+|IY{uw!jJeafn| z=2KGXhu;rne#;?BsXFw+BTve`&c_BggKvDbxckBJ!>3VyYQ-+4B`42UX&d*p8K!UG zGTQlO=du!wDRtvc8`}|%m!hBSL-iiqO~vC=JkN6Vr+!xjnXjKsvyiW^zfGpsE^2HP z>JOPXZ?~`eK$tJ~$KLJt{DL>f64SKrS;nL9_q|8CF2ocbKBn}Ns&o-n9&%${rl7^$ zlr!v5|NDa=Uiz@7AckLXmb_AN-Km0N?!VkznM;4h4@u5K6&l!tZ|}`fy|>^fF;~D0 zB-%JD^~&fv9z=;z)ACdYzo%DEB@ditCBo7yUAtQ=o^<0Cym5EG<;Br@Kg){Xbx@XV zxK>7WT;P=F^kUjfn+lW#;g|D-x@*ar?^MSO`Jp?`;kGA3Y%pUS{ zq!yc2;=Lx8CSI|9+)Lw>rdc7p#fjKM(Iftd(ibz!F88k?^C%u+;KO%apU$*(($7dC z@NJDpzL?hR8erK{{$8)EM|t_3$hhhEkPG1rX`+mDBfe{QZ|@wW^Iw{L=_pAmelBYG z)lXcs>R{wwO8@)hdx-uEmwfTJ{3mQU_zWzl2&bP4% zItS#)!b>bpXG!~c-S*iBlrq>kGEE=sy4Leo&V<`AR4?g>b}5YoieqyHMYJvF>egdY z6ZOca=T=wOhgwr@_I=F{*R8^DeNQMj z*tw2v$#5_D^20k59Ura>@}}j=)FScnTP)a$WY9T+=5&7_Cr49>;Co@AT=_)2-FjYP zVyNvZC-9Xv50DOyJ;z(2x+Y_#yVWkkWt$kMc;wa9n0($H>oAn8e+R~Y>_zrOqeSr*^K{3A-7Ce{zOhUzY3JiT{DOX`4N((gHsjgHP6)*M;; z^HnrkwPvAE@dw%#P&ofgttK_|DH*4k{%i|l#5y~SnT{h2sYPED@WzDrA}{ly%(PI?jpLynPja3F%>(Z&*mZ2`jjj;_(dgh> zJD8XC6d*F~7jNFkTJUI)1l|NPv2WS7>DBA9Y9(2Dqt%rVy4zw3x4wJiJgtO>eBDzR zB;BE(9*D_;RgL7%O>!w*=ui~1Zh$yfF6>&>L{OK0^Xh)#r&Z3KouJ-iXD)!h`#!UC z$4QgMkSjA1L7LM2Sssntt!=_^(Bwk^X})qNqv6)E3n-(|xxbm2-$W93cODUZz_6$o z2Xz@*ZP%t}bnN`_F3g0K$$f8LeG_n@#C`HO>~@Xj>lY+V^Hz{5(EFMF$VISPkjgFw zC*v5&1ssX4lXzlVVkh$6@#i3Lq+j<9-s5hPSU@Emt># zR`bmdP!Ii?x4KVA@m-iIUa)!D=1~Xf)q0HdX+`g%o{?#bgiMe|Z8^DiEq;3V^l2eo z4lyI!(|-d)v%5~9OKZ|&{f->|_iaQE+|#rBj=q7uVrj@y-qenHI4Bx`bibt7vdt-; zPp#{A$bWP23HLmwJF9=D<)uld(eUzOsr=e@s=0nun4J^{BCr6tcM+u0FY<*x5X1nS zoHtc|_3%)Izkl(Ho+PO>7gru!ye9j#EiI3$HJQQYvSd@apnC&OF@X!|m>2m$3mV7F z%>FqGZFUmXesU}Z@^-#X14P$vdpP4SEiDPT41Sc+N~iY&zjn*-qO+uPj2T2p0|R;% zTNif|k%opQR(FZXkX9RJW!?aB6sxYQI4(m%%@xq%%gfWE{pPQ(AiCmZ-o9mZbvk^N zHoQ%S9?yeaVubRuRvdDvQ9R9|0W z)Myejc|WF+4K)Z_-9JtjGKfJ&N0UQf0Hi0v{aD&}3J@24=UDY#P+gLq04|)8jsV;m zST&lHRx&Y?;-F)EFY6miFf@n&w6xkU;SnvYQWP-DfYApRr;i| z(3{x!T($b=Qab(44(|Os4CAgt1mbt7{6OhiG_pHVWKZQ37oVLSU?HaYoI9BB@b)?IuiZkFq3F%nGmAB?3V+Sj5I{Ti3bcmu1RDJ=UJsUoRN&!bG2uLX1*FJ(Pi{UBL$AwNFI955C zi-d&J<`!CRx?o_vJX&!dh{J`c#6}lRXC@iJZF-7AR+TC<8elZW?Kd?(vroI#!h(b! z&1$lAm&mOk@<;y%{qAVbq^AL7Y?*Y7TF+Je+VphV*UKzcm z#X}>1{@q(jzeQMDk1KY=5q@=b#ELdiOx9pu*PkmEi9RVp0x zBhR~_C#SC7p2jAayf(+cP8wCug*y|JQC6tCXvS%NaW6+vR4y!xp=`3VfBqTxSDtKc z;e&Q#wk2SpbnJ05a#@vvl>n;zD`K*c&Bg&hqgCGTpLL~eD z;_SQsxorQxl_F9KWt0`sAS2nM5+aeLLPiKFva&KNB4n45l^v3-Br78$I|R z^Sr*_`~Cy&AFjvabA9T1zu)J19_MkqUeDKaSV_Vu&S0^-={O(XNJW6O-1+!>?Ty5@ z78P;SWY@nrw^6VVTFXF1A44z1Km|VccEbMpfocDbRBKNv77u7dHasscZ@6;3V6FS3 zIC%V|#8>Xq#%kB?RzL6ggIk(q({2HQqfdj`QQSnKY)^95Phs0gE#6=V86Xq!HB~(Y zJ31kHNL9CxOK;1DOe~Z~=Skm4%B3(B*gXgu*I%?aEc6(Hv+V4Zl8dUUgHMK?KQW}V zey!KE>B!$AfgNv1&l3d}SCWACx@y)Ywj<>#o8H@RsnEkjULQ-94H2?TfIozOUkl*F7SlySRZn3vjdfg+Li#+{$rXewJvh0(R z-0YBj85#|0I*Xrt1#}GX0VG}OdBokfu}FJZ%;^Q+LEf@y>JJb4ww*X}a%C}}J5Z?{&ma z+x)!s_wP7NXYZ^nMiX@6X_sm3m#?CYI{&?LD3(TS0>?Ev*YyMw2GI|!UUU95l)9gG z!o&AvP=(jOdG)^d4{tq^>aK%u@3^%Q0I4PGdjDN66^1&{<2u~>*~-p7mU^_b4Zqa& z<8;SY7|W<>yvoqCs5TPJ+rdh*dG@hk^&ExgQ{`yulZ<0y*fg(sc8h+{(8|@~1=f33 z?b=@EZ-#v(q!Yi2=`}PUEEREz4lSxs2tRXgFNY`fP$#RD`&Wa3jQqZo+5uu&=oBsy zjPj{ZgTl7ryxBZzH*Z!-OsjD4^1ia@`J8P#M)~&;lfx9ZjM2B3+M-SqqVTPcAHrz` z_mTjsYUcd8))7~arV+!&qMbXoZ`*rV^1|)g`{6`HVy|56mp4vK;H7~#M+lpX7dzRF zbh65Tsz+7rKctsCozAI9lx6|at1mGVpY67h;S>~n3RM-c;NprZIwpl(S3_9&7Z$Z6 zwpPwVOoTbVQR{Gqk+Ja%T>9RKjCN)j(qj{ymfA3f@UJoDw28WwQ+C^|kz#yu@TV6`IZ2?fU0D!kFEuN zeEH_pgR@s}UQ>p~Crdzpw|X@2Rc%8^bx739O7%X+r!VGhha0uO^cJr<{uv;5_&w`Y zE8+Z7%m%q$mo0uy6daavdchs*;NivcIc$S`Z?@IoL6q4Ypq*pBEjc%De++AAF{+7C z!j2AtveTb>Sfp2M4@$=d;BN=LifH6>+dK>z@<1zN?0%{p1>^o*ii#@3apY5R3IXqN z8a^Ie;a;Ayk0rJv{f5^I_kTIhT6m*&h<_7EO8)QDxc~8Po26*fwy_lo{{JebWxmeb zmR-5OC<#7eO`XKk2D70QnU!t!$_1}hV%^HTX`f;AOZ-lpX^vB!tovA5s})$?eA>qc zuk`Lm9k)JFKqPC>YA$~@z^DWi#oz9CTNyjH$Xx$TECFMGr&$;ScNUbW{Ew!btNa;j z1Kj4RP@;^*)>n=cI<)8i4yHl%>+o+@VY#=pZ}>{@ZP?7dXc`}?Gw+&Rb#~r*)Cd0{ zc4ABX_fHA&ZcR~QbBr>sJTl9h>Oa~=U}?jQUuJ6{>_@rT(^fW9+!2|_Aw$rEG zaT+4G2<|zc4~7g>SQzwwakZWN6`@F5eLE|B*3spDU%|=lBytJ_7MUqgcG*ur<7kj~ zlVGCue6}ky&M^>V6pM*fRkNai5ajoJi4-7@-bg37FBlRS2;(XcejXTqd3*>2KyK`m zI_Au9Z88;NT3sCb`gl{MIY+@B%7^;eY;_3L)uFd%R)4_FokB_!5&d5_=_fWhwn;s* zBmVdNXs;i928lTv_J!k_Z=naAK2j)qkeFDjPRBT}4DJ1FKP`^{_E#EhQIZ;s9#)2; z&dUsWc@M#a`JQFi=vWBuy4hEism(rIO##((>S7&I4Xm>uNVs)iW zO1gu;kBvY%|Gkcw^YX6LyT_HI_(SXKsiwl64xtjoOG~L3jJdcBG1P^2*6&?h?mQ=m zM2IcI09ZpVsc`j{U|@ia8#^zv?1Jncy(#0$Y$3zuC@O_aHUu$GlPtUkfOIYZu}9B zBY5Y8P>K=5ezIa{n13c<^#7IRw#oeH05%r*XdMH_4emzgM{{zQwRz(oH3LTWHK2Y? zC%$_}6;OQ%HQ~;k&kjq3s#ZtcH;6YBjd6R5nFwV0+MmhKi@tf`?=PR(9ugJxyl>iM zYm=0?wBBN;=Hf>;P`Nybmare3jT*Z@VOt>jVT(;f_4V1MDV?#tX)@Q%R@T;ZdsRBQ zqo+BDObJjwD$Cp*kHzh?EA;!k>Xe-=*jZvozh{qMx1{6t3NPxUtgQXTx~kCpvjl{} z&v>3p3K==LP`p{nPEJe~VTUvm%1ni_4XfX8t?rL+GaZ$A!-qn|c*c(uX#jsbO9;ye zeXvH+hhne{FTUa7TG||uRFUwE?;^D9G(!$(f zUPGW!Y1z0|a5Q&7$%}^4zM$Y`j6*P$#;X$#HpMFACfgNmNJq05ZC}ZA)iym9Ctu)t z?)lv&ax2kO=2S1@JyJ-npC8Z|d-&_#Bc6t>qwxS-OoarUiZ_b{*nDWF>fW`PVeN#! zkbfc1K_VW#w^%nI{umIL>jyHB>-vI>lo%qc%0trtM@VxUwuI(%6Y4-tA+0^h!y6F| z#wANX@^9ukFtVz~K4~8u!p$sL_^H^SH$1=!yYY$6IZ=mEek&_0kEiF*1*fGQsS`Xn z9^sRN5x&~X(0F!iX}!K9G|lq}E~B@@lpxnxz)UHP^8>eZ`0Wu_$M+iJ$F zM8u;H0#-mz4LQWFLS}QDd~Ph%Yes)|Hc2?MbG|;j7k3asEE^lU)Nb2WeUu;YHPNrl zJ}}_ADlVm)Newd!jROfS&#$LTxO^YA)q2-Z7A>DpW+WWbuArw;;b`wvb&}eVQ$;*4W$*Ec5u}Kv_TKLp|lXe%i zJ34}e&z!#dIJki6wk`<@<%H3 z_7qW(kz)A~GF+?O@U%X1LXaj}1u>5lZd0GS!!L6Qy`Hh6a+2ao2)c6YqG5tNB4>ZT zlagR16S9{r`ha?TZlF5W)BR`7uC#k*HTBBm8edKM_03pr#JAoRh*aJ|x{ZnHkhizL ze5-RbNY3R;-Dh-LXntkNig>eb%K6sql-6@-`7A3zr$njI-Ds)b^3x}QN4CZ474;_b zZ;{MP7pio-&Zzh@&wI88obFOc-;)_@m2@{xUod}K{3Xkcm6>Pz^3F`&Te;FU6VR|X zLX1Xdt+q1dA_J9v-Z`_yJRO$J%ukd)lWnT$ZHTG+_LDEa1x%LWg7}iHFYH20moLR` z?hd*W(b2&G$2T>N1B$^lO?;+|;nBgt7oa7TSsz$xHai0?1!V2zPhKcp+WBL82~bg` zJ9W2vS~qxJ-bdzzi|uF&dSOi zPs`Zgi-*jb{-rh4H$|o*-ZrtiX=w;Ap<>b;UY@0e!7hDlpDIqKv9U48L1+#gs{6br z5(b3v3QO;?JYw$D-3aPo-yON|JA)#aj^zt$(OG+b->=Fk-G_f>^gVAgW9R0Mggi}J z@6&T+F@S*yz1T>dxrAk*RCogy8Wbz>!?21GCU?$gN&qPmr==`>{SLt=DXPmN8ll5y zIOM5@h9kMN?fYDzjK2&4utKS;3tu?c?fHaAJ=1mNFcbzwYrR5yhnNTEJN#^{sWn59 zL7W($?c=#h2Baf3H6tPS9hJAQp1a4gL$=^)hlwn&=YHN*W43BnZhGiun$%{$g4Ocl z!s!?#953Y5L(vc4)1RRFE4KA=b42^v+0ZkHfwTd_>?|!q<{q;?$lYv~TUl8d7}*sB z@Weofkx^is9ZM`i>JbZ0r{n4t1^op(W!dpo969o|=Iup)W~r*cvVkdkxo2O&z|T!P zrXVk`FT@1;Xd=8}-ll!~XfDC8UH_PFuU=$p3=DIOjm1@6mK5I|-2HHY0J*=zHR5mE z_^0XKLI!khD=XX`d5`#LXo7k6Q<16B?F5$Tq;z4DQ0-xW|1j)5wwSUip!y^U7N#kYB5-Z+m=N^A?wP7fai%12%13CLg8t zEA2X{yxVF$@Yhs-g~7A3ggabPZ`e}`FI>pA)!1?S{Mz$NKjjH3d)FfK`cURpy#2H; zkl?I`<93AjR?CcXWo2iE@s+C-O-GK_(hODy3_y?b2mh2C=YaMs{j_XG`2vc~kv5nQ zMBB?Ck<%0BIsAstdQuf&5BhPnb7^J0{`DDpZ1hmm*T|@yHI`@M${{?a|IMlLn@o-1 zG(UiN-?X-SVy^2u$oC$mvTrymM{ztuIswaqfePXQh^j@vM#Jw4B-fi=0Caw~ z9jkv4VFSM&?jTO@L*#q(>wcOSEq_lpGBW;PSbd5xe?Y`x+_2J6=SY0}*l1gh1hup! zU#s-0$ES{)Dj+4{)%2t;1bnRoH2$KplF@irmzdK$!&2`f`l=k`SS95bDY!V~8yNDB zu2^@eKnYb>Z(u@h8Fyuw$#$&U8xnF|hcOMO#nHyB!Ie+9-tYeS*yH>_=iAlA$*+I& z#vLZ}^5K(sNFjiQygzkXTJhF>D!6QeK>w_I-w!X7Pn1nxk}W@few%ip?8;k>AyEev zrlt}u-b|4uCNh5ON`8cNUn=_d_Yfx+=ZA&5Ny4@&G4aBYBa;BEZs%w#UcOvqkq-z| z?!tu~smE(aq9c1ODn{n!1{7i~w>IogTTGZr$4HaL$~?ZFK7bAE(^8CtR>NXa`*?)t zZ62FpXCej_unTa$lDWz1ZFzTwg&z|(5({fvLq)zsQf@ zv3qdf$d2-*&3n>E?TVWs{C4Yq*n0yy-$bt;-(Nd=W671U=O=5s-<8k8^Ri+ z!$sWKwK5PF9P#7tVTh5IZ(XI7VoM$;QTkFeiTuNU)a_`Z;-%Dlx3MeZq)Vx)T zDb4!BYRtlx`I-h!DXD#74P+3NcW-RooXkvIcc&abc=DLpr}1mrZ%=e^^v{mucCWrA zb5AwzqPW4`fQ*KM0}eW ze190IY$v}ztAAk&HoBN5i>{;vtlaRJRrFJUMM#b{sK2+H2$vmEDKO2`Qi&Fq!|lRZcl7k>=)KI@T0`z>ZxfTF zI^l#raOzmJ`1hns+_vJ(DJrjMZ|B>z>!{Z@>-3Zn@X4KuTxM~{D*zW$%*aVQIk z8~1GzSx87tUzi{3@kKidS5mG-4+!eHlM0T!J29|c(>MhyHzv%^Uw=RY59Szq3X0H_I( zjg}4_cI>dmg7Ajn(4~jo&U+U|@tv)7FLxPmzW;F_d& z6LbKox-vo-Mc=TdG3{kT_+_(M-h~StwPWFz&2^Yx9e8Cs5nhpzuY81^J*K|i7J~;w zY<7#9l8AM7Vj)LhZbsBF>G#-RsR)}{nCZ)Db}GWAjzfXUaP+dfnBzaX*v6=a<*bCJ zh@_LeZpyR;!y0^%uFh3rPWOfPf5qQ$OJv3OgJ&GKk!@f0j0wBfj~|MmqO2Z|1j>E&SKqxO0~R=LHcW>BG-;^ z&HkeJa_zuDx5EY-G)X!ctm4d)i|;%OV=$`7caa$QJGitKbWMQiLytyBfwn=wMs)#q9L} zY~1xWZ+JoO`n^~?EVCq`?kt`6Vqy1EP@CE9?}=77IF}#Kjq^f&rX1~b^61e!IFMKa zs_T|wHWwX3q@yCKQB;*ISMJl3EukpCdzblWmCCD@Jnq0NasA;wT~_8Yml~!{>3tXV z)ZcZzQR6L7v0Ynm&BlS zMK9s1)2*bgFZc4TUq`VAg&$GV`cZPOGyCw~FD1*zDhErOb!o@F>Hg&L>0di=Q6=ot z?eOJWpJSezN3 z|BjUZkExXYv}~7HPnYhQAkLXjaqm}^Qr6R34#hS336dGE<-g*5o1x}7NPX^O=BAVT zzN0(j6hJI3njaJrI?La&xO9oe_AjsAcLvshzel(4BiM%f-cvH^IPhG2*RAD*)pqFZkwDn<#*%iq`9i5pONaFD4JbklPTo;5+86rEI0PbzQcPa zwXM|>p8Po6vT9Yl-|#F!aXRsA)mOrIPz^Eo5EG)iOIn>=9x3D45Y&p4-368tgCVu9CR^hv@B{RVe%TAoSULE3Id z65moDo+@~c{=~}z`49M=E?uwbpZ)UsE2V_+G26m($$k70^|vlIgnC~N4dm2vWxG{- zA-jKk9B==zGmk2=h5G*86k2|jrj7SfR~~Gci|Z+FSD?KX zb$?4V1m8jVJw{Y5v{LIvJBPq7o&Q;Vp7Mv@ljNs39h&hnG5yTKX`&>#Zpa&?Ht=38 zO!m^!@yl}o&W);b_foVaH|gb`(0b85-CRtPr8vjmTw?xyV{E6#?)}?pG+>GdbldWg zu=xEc3B^f1jsykh zbZNr_uo+xR9x!n>W`8_j^4VhG1kw7;d9n{B0fdLWVfdhfyqumoeL3mO`TB*OT-2s}yC68mEI?+h$$oBIs1n)^6Qz`<9Niz(cvT z@qLKpU7^yg4cKQT(OU8DB6FYUEQ|%a&8q(kd;P)FI2=fbG4P5{8@#S|W;PL)9cT}L z$Ed+&VsnFB^P}^DQ(=*CMO&Pb_=0iov6B+soaX>zX@9gxh%0#l+=zYK9zQ>TN=FoF zAiZE06AR+i{+M-dNf$sg>OMf|aPJexe6D4D0kc-PX}I&$!II!f-na--N3y!i;baBy zrt8non>l$2%sfPMCx^Z(JHKA5kmfVh#RmGSOYo+n{SoOu(DI$uqNJeEqG z5wUouvZ_iy>n8CuETpI5qk*A8R@eQE?>{>_Dn0!otv*Nb=#}{N6fWHsVgzjrp%6I+ zLrfr5+{Dvxa%VxfZ`EaO&5!qw-r-ioRr$3U#FdmhK~hUl=xd+r=|^Qs9|M`qF=@gh zAQfn6&q2$+FfKwsMtDO2+*J%%3O?xF4)UU3`v}KTF$%@dn+0aPaK{+>@3>UM;#)))2)00}R^lSlibb8Xt0 z@hb;jp(p>7GyJ-~PNE#M1JI3-dIuMSj!$<_0-^rHEM5)(ve`@s5g2TWGys%Y#P}V3Aci7ylGj^LuU95FWSeE1+q6u(L#~(4Ue} z@Xt1#72L}r>GAbG7Z`t_Oa3(P@PJ}`Ec+E)H6uZc2^DntxW2x<_e4xu$iB8jB znkv3_T_a0>fCiB&iYhAQ_l2$gj0#|3prbb_dn$)N1zQO;jqwKPH)!e=f?M+Ua#oIOEDLvM&1hi!?7vbYz_*=r7;r z3z3U&_Y>4&^k=;`>zL`ktuuIs(!OkoOY;mr7EL-E$!YW6i+d*F4Z(Y+x0X&z`J^yQRE3KRzoP4% z@b~0HaT}L${g*7C^i!=etm;Nh2hw{^v+%7mmQ+Y@-FNn>{toG!7|2Ec|>4rGpF$9Rm& z%D+0e=z_yC2vco%=%BV}nByxd2fQ}pR9v*VD#4@kkp>PPo|m;9#$Jg4fr0Z>F4Lat zxZN5N0*1oyWa5M{lCQM;zal`Y_*4x5KiCIv3$z}HRt(R{u+PgLLwi- zH8x=9Zu&V`!(gWSxNGS5GkU!{O}!f#b#GL>%HAkswt_+x^m@*1DD8}pknmM?oeKP@ z^SuKcUaDpUIDRDKjl?;#Zy*y`D1?$z(5I=4~pSp#-Z)UHe8gIjgeC;>60uivy3w zSLmDW?K#1h*$*5$5wmh^`MK07$oP_mayPShRv7006=r!KtQWK(Zo3R01?_xulfkM6 z-ZfFDc~KA|K`+cP8l&uWp3{M+5zwS}y2YVkx{jo!eX2SxQp9S;x%v{k_RYE28?~U; zTu&G(UXnrFpvH(!A%Pyo((A$At+}J4W;b8SH;{%>GcpGCwp_h+3YYYSBO^rIwhb77hu9v2nkbN`U92AhzVsMZ`O7wkLo?umfDu$1Cb z-?z0XiINOd=*;5Hy@5+n_-gyXl2U&~xN^!HG>*uA|E9}ZY4tSzw`wZtcl=5F0lVjW2P(fjko zc*eru{hzzMyKcMDu{Wmy` z8J)=b6B3Lf;y@kcR5ZYdo8Ud@X3`&}GrOBKzv2SA$kW29sPp^`e`2>>gI=Zow47_8 zt#}~%h)YTXF}ngz*ZA=A*M_Tc5Od%y*Uh5)P>Wp&jU>ow&?wqKrQnH;j!QdtuH*-GH&>_Ah}vY%G)J&SOvr1S{-{80HFe? z#x_@%Mu?XJeozWTW*KAcvXvoN6zdDqTgE-Be!%dk9sh@kB{$MoFh9}ahYS}?mISY8 z1cwAW%ny*GD0*Bu)$^!e?R_d-^adWhH)5K1f|ZPzon&lyI0Kh5LtUcxAgE(sN9MAO zYOMh%26D5wF(?KvJFHS+ca#ToO3(27?Z+1`AP!0-_f~y~lNGh(4*&{2o31CR03a9q z9Uh&}!T;0`AUybWB)6(jxrYDI#;c@D zH-aOA8uLpRFoPv_iX4YAYe`+co$$u`ZPFABQb(=&5Ngq-kJ zHp#URy(?;(iq6ir>a6OJ_m+`9_yklx%s{&x2G7D}?9t*BGEiR1tWVt_YW!=r%2~IT z?f>m%mC963GYM0ai@N~^*jB{!@1>QMg$J8oK4m2oO!rF6u91SMObM>;>%2p|c5Ihi z_z`kJIKLk1nHMbyp82_!(H236&sZ~1OF6Kj06K8;#^CH~NA}RAbyu0v<&_))O0U@C z;xIPUUMpj@{V|pyi`zUqeiDFu3=Eu?BvipO7qjDueOhh+K?Q)RM^gnMAjqT67S+%# zqYaRGA)het2S2K;sA7JqGeP6B(jIy`Hpy-0uG!KNUyO%HD4wV1o{2^e=*tBk#fS^0 z1qVTKr8*?+@%pvaaHHPY+iC*nvnGD!gvCe|((d0sd&X6E*A4gajm}SvB(zhlft`xK zb3|nC>!$Xb-d}sC5mwPTC_+Uxdgg3=A3udgvAEV9k9)qm&i?u`WY_w~viZ13(Qkj1 z6vMwSwV{o|0U6vFZJl+qeB)n^;OQ&pDHrza8O{A1dWs8`V^ENamE!R-edsx~jQ3yF zIDn~zvAj6Rw@$s9G$ojY6)@Y7U}>OFy}dN$;?Ny4lQQL)cktDfV;=EoJ0h#C;Z&g3 z*Ia`$3@7e!zDz==lG*MH@p7lok0>taL=a`OZ&!1kwh0N(!QIi;62WfqBpMpppC7dS z`0>VrPTc5D-XB$q>cjqr#eAwKtBiBL1C=IG=HdVq5nRgsz~Aw?yYhuhb0ONf_9pE! z*#Uxt_3o!-3D?o1!KjJULtud)bI#nHH;s~F*2;NABH*;u`psmQWs0q7dpG>8h}l7T z93&_O3s#0Hc<)ZO6i95XswUpb;iJWU}1%yi3-D{?N_v}d-lHJ$Tt)w z0WHwTC{YatS{NJ$ae{Fv+gTd|5yMQTTj`FoAT@&XgcBOS$mFyo^iD@DVQ3dM2aKlOU293Nk<6ZrkA zIzPi6e0J{N-zhzT{gygar?McV?ee_NctBY6ysT@Z^}5@n|Em>)WSxm&=xLgaKFQb zJ#@`+Cv2M_?CY#mznO7el7;VJPQ;6OOULIgK45x9yDOKaJ2o;>cFR>{rYtu=6L|zK z4@N60DGXDOWpWCZt6<(t0;fg1{no!P3_LsiS#0-FMGQ5V{dnL%{0!EM+W9l0yNY$I zc^jxXf4NUX^fL?H^FQ2Y3&LM} znL&t#NCRi-KF3%cldZJ06IodTXv|3hV}g{9F`O4Od9R_NrmZy19 z*_fD2b~{$nLGJ`Anfp1nC!7CHn9bQ4hfrt+L(+FaPJTPGtH5W?I1vgX9yyH}rzR>o zJ5gp##B80Ix4Ds|w08c>he`qUHMl>p*;Y^K_G~adr*`eU(({){yPoRyxfw<5J*@kVAo1Zvq-Ic^6M%WVG0r3D+M0B2jn9}5)3i}Hvc^} zWTcb3>zH;J_J5!p_EUr3gF**PEa4keXMmnQeZnU1{=}xV^XSUN+PRWZ`eaVU2APby z*)g|E!BG7j5nzugoGN0@>3s3)ux0FCDz;l$!jTQUsttEb4npV#-%+JcJEwCkZ?I9M zxA}ZE81^i2`E<9&4@p$eFS|pvkItL68Y98k=m#(uUaEytMikGo)pXChiWUKd_)I7Q~4l-a2sL zJ98aR!Zx3`ES>GvbNSs|Cu=@0nse7>+#uiLis0clidXKtEx49{;+-CyLA(5`zmx}J z`6M|y#vUh1ovR&NyQW5Rz*d|;oQ+*E{+^7tE~$~zo=`>Qu;#yAC3@#R?Sm}u3B22F zMpWw8oY^7m;_`B5JS}Yq$p=D5dW$v|*X~1Z23j^zVgr@*S21Sr+h)-fnSf{`%@5vtZhjcv_i^QbOumTT0IQUS>9 z4%*;?$&ZmZ@bufKiY4d!T{1p;`}-d`bLJTkJHxE-)QohK_V-fV)^nWm;!?oYc7o3J z@UJe;9!*vzS%O9;}AsgD_6T3fNL zx(}R(Pz~(cW!Zn01<-DZixWz4L4@u=Jp%EbM~sxyzC(xHrOF#&;Xm4wE&&$)Vaaf0 z&PPeK}t!zfld_;zVaPg7Q|{_+kLR z{$6wtb^db$(y+(ruJ(0qM>laW#|Wd zZ~)*rnitSF-lw-XQ{OlR9~Za_y^wZ!gi7U0uM4@u=#xhuZZ=XSBj4vFtIPUvB0&+l zBxF`%+^=}mV0$)a)}8S%_&o(AChiz^+1v?7ZGxhQ;e2qIeK>-E657WJbc@+0TT$_{ zg>+c!v(w#Df4f7((D4GKCFZ+?<18F3iHa~&PY(;c3iqdZ+OveP#fB9#`cxmzGTbOg zjYP1Tkw48VnJg!6(@TPrjvz6ig9dT3Y+dUorBlK^$nx1S}0)7fd!JS{rwKh(r=SA79BlQ+oTm1a%9A>lwOUx+&bK zm$E}rwDOu`qPceE%fIz-e|nv_;O$$b@ik2iy8Ct#T7IEt?uqatOj6i@$bKH- z=l)?AC)rw33oqn95v*mTQOlxn5(sd&Wb`IU+=G`TKHRU zAc?kGU_8h1A`Qx*80UX#0PT?weShIci*Y_u7N5x{+-`oe37pwi)wq86r#4q`_`ZPN zeXfaYbqzg->*b+#zBHl&uOc8wF5Ra@dJ66D{PxHKq5#psvnF~T0*wRdhV$e}Qui{r zmE-=hBdsn7>Ig1KEkNa&F4!Y-~ z4g;@3txm|2QQyx)Z8YLt%{ov)Ei>>nOb;zS;meFJOH}R3#S4kd0ISk7OP*B^4pfzq zGC~H7T@>k4VPZDYCJ?M4=wwt4+n>)7!T5(^2k+UmNAAWH$9C+yHyHIfHtj4ZHbnXk z$5e@e&;spwufv>9ARF@AA5Fzz5`kzuZDa*4EmxQ7vLQGOhdMY!rb1fK={Ap51XLmn z)nPr$i+FcI0%T7-ruVyIdhd_)6%dZ+!kVTT#Kf* zEbJyYHMJpCgLaQDDl41b_4e`g9hy~OHK?lZoak~R(fn>%{X=`Kr;r?JM;5)Y0FAC=O3VMU$i>KwbbtoL_zdkom{&2m3 z40D_s#9s!ML+gaRbWgs6d`TtmpMZRo8|8jI{r3FZ{YA4s3)fdNw@}VlwnwYu?K&#V z<+(Oh?29p3Hqbc0NC7g7gy4ktbWClw^14{g{jV1kL;|MoPF6i3?N}Vzo9rg&tHA0K zhy;@tT!Wfja<_A|U@p z0}OMXRUaHOve$>a8WdAtnGWkMUe7;tMFmOj^t$Qc`=ORjS7y6uH-$mYe19-~BE69Z8G*n}sXRWk(1;xJvpE@CQ!b zGf9rY?ovf7i;uwl##aeMHV}?9ju{1);(6*MEw`kU*RUetzeRWSLW)WOvZZ#Xx0R>* zSQ|I39M;fi`I`&9AM@yf3CpZfa3#u;hTOZcGpmQD_fe4n`S5xIlGOg&BaKiAJIq>R zaI+(!rP44!nrNsF`X=2;=Gi6VEGl&}hUZdc)mMFiNXSexdg*WH=TkPLCe8YFA3RaZ z@r)47A_qo2i>`BCt+BVAT;{SGyXSDKReJ0I70@axi`S6|n1vG75KuTsGOGs<`bMC! z?u17P>Dp4Ev)+#KhqX-mS^l{2wjN?Xdg;i~OBFL+Uk0E07(9DzZ7Cj?rIHk<^>va2 zR$n2ZX&!^lzRbU`avFqiPVA?-E>X#)Sec?!V=zTFaE~+9;A?^K+}Z}pwfGjZ=P#a{ zM*O^bcWP~V!O8pihx`|*GY-Wy!BCl*c1qJ-<&P-Wxfi8E?~2@$9u>C!YJL7UEvC?z z1XR1Dq7J=-F$OqcRaG-SGB`aqFKtr2b`7eMeNY0Si)EmK1q%rB)tH0= zu7u&m$Q%x&PP2*X&Tb28q!qn=dl91y-OMm_08Yg$g*|pH=e|kD#zGwlzM@^>9%%4% zd-nq}beqd`+hia?GG1!Bw6G~ z;-?trP((KTyjt*YWgT!lrpzr3j&>i*;|(@vo3+L>0SoNfz*qmJ7db0zkk6=o9nbF2oW5w8U`;n##F7?I@%TVbeR7PJqu!S+}jiK z?%M*YeN|G-YqxBs1FpA!AH%%`dGSf#^n>HA^uHQ$`A`ia%f})=@9(DFuV+(g8qfMR zOJ#x~Iv`+MSy>Ld!eR|Z___eogbdKz$G`kdJJ2^6jD#x>knc!K6Sj{y9VOCc4z>iQ zz52&4z>7KE=An2r#u5mDN>7=G!V6PR2!xPWq|e`7+&I1fhOG9hzO!s+Wo182Jga+| zJmdrd0&wAu$;HsDYa?dG8xc(hG&HP4qa2D@?qAow6ys6ozf=2L)tH5EN+DNFNmWWy zkEu}1Lz#3F=|fVwXd(2Qy<{qwn4mVpW{e>!@HCItg~bt(0S6lG`OsP7W?WC%H3o zgSE|To;olwwe##q!W&cjV~ahXaVmD){~6k+>UKAFv#V3pM|EpWS!VNRwZFf3Vbf-W zp=sW)>pic8m>PDjEydQ|7tTxbVDziojoEJH9J!r$^Mmf2!e=Te#iLnZWA8j^LjU)p)#}~(XN?jA+lS(ZmbG|+4Ket6 z&i&I19r8V#zHo++c%V=+`Ik7`@ovDHwj_qWPQ7mD(mj5L=e`ml`F4eqMc1Ei*=2BW z94XJSiXOCelrB_`mG12vu@!E06Km{jAL@QQ)jfI2uBWMCWOq+OP*4zTbU?H4_8v)5 zNNJ6+@4GAX`udPYaN^vz>ANWRKYiMr->sQ({oVC($IS5sCz{CWbq}`(($l+lc!)<& zJzWdq9D6#_6*I|qO}IScn8{Q{PTjY$_emzxYq>VlCas#hTG>4LpZQeWRLq2!=1nF! zqNWGNofoZCVo!Qh&w&pwK}dG(OEy03lHL&)!Rao~Q^a;l4%Ttb83+trE<3Hd(d5&- zz;jC4|AE`M0Ng(K99d!3NEjFdy?SL;#=U-iO_2uHBGDOI{vP)t%ifrjdmyZ)Dz{#c zi||cz`R~J&v$0(*QGAz}&Q$<`;L8<5#PJ)i>?qW1R_IuT(t2 zN}G{+PA2vr&knq}5`q8aX%Q}9hdBP1fcO9X2P3rDEz83{$!-mJVFb`OhS*T;$viHuGFK2N>5!^-N7-4mB6bCuaR;^p*5Z z5cxZV>}w5Gq=d)D@uqBP52H2aWwWPM>kzUapnrzq>MX zxmkB?$F1E^P<}M;D%&I_M`RBxb9Q#~_(L3oSFhUzKd;$^c|_3B)H@K;Q{u7+ADG~y ziNmG1nGnFkrRm6Vu^==Bu(Oz5Sw}iFha!zn{FUo=KABTM!*u%yNy4lF_um zHwp@T{eTZ@SFd`Oy-AV1&c)5&Ws3&^E}?5`CEV6f6t7>CQT^M?S?pcp(1p@AI$L z!*JX9OF`yNg7OQJg7Y3dDp1;=O7VU7u0GTTSyK?rC%mP^n4shSbxzZVEI4vu8~}GL zh`Z?km|#N(7ap|~$XW1_3JQww|9Di-8G!-`LC}qJsq3g;0-WJgT%u6|{|;O)1|~3Z zqG8-`RknWb{(U08;nT7xTa+-IMczEw0N6?JyfEZWG)WA39ZFcp07v3P_W-0FQ!Cs< zEjyb0y35c55YV6+Mj%hx%;I3s1dz?znjq*9?eC}Mn|OmORDdApma|9XcCdV3_mk8fi|*%pmFuOj*uc7fMSO` zxNiS2P$Yb%|3ey_&j?5Q^%g(+*NXttzt_ZQ;HKcXI`#l$bjO9feE3@>!+503ojeoX(fQ9A=fme%Jh1`u$PRt}*lP{AK4mO1L>;>~_Xl>#CTYrR0`1P;XTHv%qz`k%Ll{=@p_NDIP@2EJM005;B-?E`@fc?=w=K8Tk5eqYvZ%~0$jPejYDB`MV>MK`nUdXr5S+1CNYe4^v`Tb64 zGKleAaIj#^qeXHk|76Mx@~&vB`*P-$%#-hB`8^Yqus%bsI)?!JsQMRFfSi}QUC0?3 z@4`CB{ROKCIE+rvJT1GGu85Ux^<*MmmVmIpJMrU85)MGiwK+2V4x1EvS?ZDZ<2^rr&2_0u z@86c|+-~f-nn+s==HXBr_!*=s9U-Uj@C@v7_JnkTuzCj3%dzcs0~`ut_Og1L^VNNV z29;MpIFbh~&^L@H;d7d2?8ZbjA#nT+o${$u?9JULT7yR(K1AO+m#!yW&FZ>z56YW+ z?G~mBEU``1^Qy+d!LRFC(TTG-&*)-Pon5QYX#&3o?ovko2nm6Yl%#iKD26m(r@*qrz3YZl;+ME4KN6}a)-=j?AUT|X|fHWo0m zwgIdy5*&pr*9xu}hY9x0_3AAJV|lVz*VWE;VONF!cD${w3%f5Kx|^qEr0EXK6&`5I z>#kfXhrJN$0{;`jA4TfXB0e3G_Tgl95yY%92;bjjGs1UPjSgrD5#Ihgo<=3=7!hKO z7XWU0W8cNX!0~FyF|mg%_d=g^vDQP)?V|u3p=gE+P)cg;Io$Szg$0~2*svJ|9oQfn zjT7mq2|ikv3U4lm!Urr2nX^e4q|8Lg7g;4AcqT*FeNFB*h)dm;vaD4vSk4(-J#wb) zOigz8ci!XB^X%Ju6JC61AK+TR#z44U5av9{#-tjFQ1O)y6=0aJu^xY{AeRKB>yMEM zhaw?991S-G?~|Cn5l%8Lq>yECCwg?iDo@1=1};uG=7TtQkv&!3vOP#M6DG6ezq~98 zf?nVq3PsAM3$J2Al`x+HP)5kfv_IZY04NDM6$6q0#-P4a4(IpBBS7@j>C@lA$l-wQ zwxijjdN2umRek=B@02!aMNeUT0Z54jw+`CYKgp36f12Ys-g*@J)CPONO*^6OO{QOTtLy{CW)(?@MguxJy^5Z-_RS;ntd_936M2tvZ zhQ8cR!)#+sXljEw30ai%rV$s!cbFIvvQt`CR`M@4a4sT=bzWrzWdTM=$4;E6f?dm^TH5{1oJlY^Gh6Rw^NekR{j+s7iwCGz*4g=(xa({1Clu=%fV>rM1%@*IHp zo29<{F4=1UO>o3!tui=3pwI3mKiDwBUiVLq?5duAy*DNNGc-d{7b*rf3y~j(mB8tQ z_-w1zgU0pY%+k8&C9^q2rd7%Z7XZuiN4z*JWf$e)v25cX7cUZ3a8NwsV1_)51bGe3 zwR)X`G)oApgX&fjs2!CI3?wgpcP!KX!ur_en|qo5h0r9MoR!jlPQ~#3V>-k{ahwvF zOxS!6XodzIrSKDWX2F;ZTm|H?cXTURE%m5F8+>%@0=tuY$sOkxy>Aq@!;A7nE>j;~ zIruaLwC_&yNvQSqQ38)!J-OSn)5z@6@16fO5x*|(V4Lh;aHQDrG%=AY!GjLu-x?6C z2o@)HX(D!SVq&lJVy7fHykor?yJ6aha!o-0%bvp$F{%$Ao+;g!84vplFr-|8fNkik~|7q_# zyqfCPKBEFEh$w;xQU#=UPzVCjrPt7_fJkqlHya=-ARwS9f^?~h^eUjzOQ=dH(g{_1 zhk4@r-TTd5GxG<`nsF^mAm=3K|hJ(y<_uJNxTo2!syt)(#pkrfU$b(&T+&gSI+vmCH9(lG7525CYT*6X2< z35?hjCD@4h=SVl^F-fT0!5)~20C5ZReB;vAJ1R~mP^JQA>TJ-OEQrN4MJ0>61c!&8 z1^Le{2GKiBk-q?1j(^5aF>!73kdIwloCGj^uQTjo+|iBnPA-g0;FgezzhcQsvTf+J+03 zW;q2p*WQ;`p<=o9m+^i^dkmU&ZpF~Kv7UpR@SOK<2Fj`l~9~|+#hstPL8141c)#l z*&J=m24A42Rs-z|L&DEw)tyQDq8oShWK{~&KlL}cM-;gp|lb4QRb*}Y2IZa7< zFdL!|!94_zARyM|@M&(SL`d9w2|5dAvRag&eH9|nFgh;*nomlxKZN|ZZb5rAGBxK_ zat@;a2h+RrDAKG`nsLvQx6APoUsG3tcd>QQO`DbrI?)(5CYL-~W8{<&7grD1O5e%+ z?dGM|;K(6@OnN^W=$D$BTFObn`79L>?&a&99M#*SVY;vMA(Q}&-Ave6FlRudxc7Qg zaLsLZZE^thfQ(}-6z3r`;Dct;%wXM~ol1hx-_iV}P)56OW;(s_8dS^x?60k3+|+!r zu^We8lyZFuZl#M`XZ$u7)j{#^!uwM1uH`^T_+DZK!ayDQFOY{C#^E?n5I`gW_|%O! zG`>j$#eI;wxd-d#!8$|mHQpOr)xwaS!xSbD>vvl@06{o_tepa?7Oj~3ivm;e$xV%6-dqw zr2rIa+(Uha=vR$DpS=8cu%Wv|Ng4h`E`c}6sN@Pac-9V=ekI$0{xCJ+d=sRAxz~^s!s*ytG*#kgJP-lpoO+HholqR!HKLWam zXP>Z%;&%mx0N709Cm7JViKOdl!c3*OjEzzy)f(dGkyh-7KMPHdbmMcIonCV)YNdEf zg-ZpeGb5z~)J;2DpX&zgT0F1{!v;C^eqlM4qS1E;_&wD;#U`liZ(hBDfvz@0n3ibdXT}t;WQe$dpy4^Lq<6=c{I-|zIdB7@-p>#`9v0j53LO>(p6uV$=#070 z#Rn#YFlBp>iO8efc8PGCNFvh3E+`ggp@u{ZO#V)j(&gxDhzjMWzb9KS8uCG9%hTS0 z%?*=qFyyvpRDv6NM&dt0{xA7>QYE6K^BY5d>W-=8&(@jWQmx!mBA?@%9x5HLX(*9D zCqwmj=FPzv9vId(7om^bYzUroYbe-cN}RvHUQ#mjVdV_9 z!;`1{g;aMhbN?NS0%&Q)ZhzKBp9e!4(5|OZ9qh zoO&d_>SwYohj4#G$$)ABBa<@2@jtwjVX>|k;BF~QAVBhb`yo9xE=cF!j9y^7NbT>b zcnxmAG{q?QgB>?uhPnC=Lxmx|myI=`Ll=~itj__14-XdTeh8|nAZA;Lp7?tWD_K6~ zaOV6l*J3{t=FP!szw)rC(Y*~i8ttNjg4XKB z@!xHHWk6yuL;Fd9ykT>5Gb1Q8gS0u6uzN~=zVIMjgQghd7a#M-r&?mC7^I|4P3gqM z#6Y6J0`lYnG&w#T8?PKkBjpims5q*R0g^o5dfpI=^mAr2IuxLD2vCHq1Yn%y9shdo zN$6*9JAT6gbSqSgl0fJ4{M(M!>}Y0OHxo&XSupTGg-Jqyw5MAX)JQ~3OpL>3k)RD<|5(&*f&kniGeN?lx#;!j341*glLqjBglHXPq=3P;tjwl_A6W%D&L-Ews-aS4enA3p4^blk5`@R1%=oy*fxGxT|gRHdU zF7ej#ueQML5tXp>hWbb){19;Ma7QKqlR@Qkkk-?|Acpj6IRb`3Vx^Nxp-ER!W(V>w zC(_1DhL^njcXlQ}JLP~0qXCFPbIR4ztK(q|I(m9u8L=$*0X8VC73iEb zd0KaL*ZAqYvcSNk5lh=A@2b}Q<8u@CdwWq?Ih-ILQ|~C?F&4OO!T;2aU@%9ES&`qT zIifvjTZ}hhL7DAu8eCuqz{jvk<+#D1tLcyu?v}^52<9EiSpMLW)}78>S*iO4=VVlK=qHNYl@VDd-AhwSp#aEF`Xrtl}!Afg0Caa}JNyi(Pp zdafk$arc)t*|$Ef?d~%LEEmpRfeI80m~0l9R+-^?DS6XCI}sp)twlC%U=5&;6#M6k zz6jBylX;pZ+5}xwS#al*O`MEf{q&0Rqu8XDJ% zk^D+kj%SY=$5X1S-@%fqsjI8{iB5xRZvz+6wUYJe(;NUC{*GdgVtwR^!AF_MxDpbX zCls}%2su<^t}nC5@xFsW&cwoyi*eMt8eLsJ%UPOv%Z};`Vk3Mj`9NZm2Js zXB4+`cLG!DURZrwUt5D~FZcXyK zxl%Cm%$Y$Y8W8VifHk(WItkbhDu996 zCI?0)CJuSgrNQK*nta3oIOxaY3&t&GsD3?Pm`%W107P{HrWZ2Ui6M%CSOJYReOroe zxfp(lH~b8^7}n)p;pgR@K3RF5 zvDLvQkLH55(Re4D$gM+h9*twOHt*ov3|pyi54KLNv-(3TK5~r_X*Mz1tU)_$JX)V; z>&j{zFTQ-w!K8G7l0@TH7SoqT{jrtr?8H;(XrBW^QUN#H$TZHEudl8C5u(xVsU2FS zBt&k;TnP?)q28n~Nu?ADK1ys`=C$>6AT3?F7wT0O1ns6(;7_e>*B!?OAFZ3WzZQ5t zG_-|!)>UHPZx75bDj?eN6yhT<21!*2QxQV<+NU0qWd`z0QtP6oeS(78hNuBaKMvIZ ztbEpIV4>3;A8nKP+*ONhVxW74BP_;bOuWt@g#1BDKa@1!MRSUdxSUS|s z(FkAo@@shcu<84Es2o+(RCc=~L%4e}O8+(P9pEL{h_22gVKF!Wy8KI_@l7kl*bf^NK zZ!_O%5vXd`*B+ksIodt`(?G&k;^5d&0dpo4(_C@`g>MM2CDa{6kY2rka9#ZA2GqHR z!HWr@GE8Q^yQrD=q|;BD)QA1MBwnj#KF{>telJ1rI3eo|FIvyzZM_8}GxOu-r*{C5 zWo$+^tG3@|meqTa?vk3X@2*W(S!*lj(N2?;fX9F>AW``jzI5$ux&*1%*s#L1@w-!e z`K#lNFJRw(v{@K@Y8+tfMUNK$ z9h>kt&$xiGZTvFD7wXU#akv5V%pzTz%|{m^LkCWTlAr8zEzA2bXJrcq)5*T_4YSB1J&6%y!B8$IrE)YvQdxu|ba>y!_$&72oNi$n> zd$Eh=#f+d*({IaXJr>Zh)-Gb8##`|UNgGW$zo><>iQU!g)XgB1$>Z75n5DmGu@!SNVWLa>4vDsP_!>4zE zz;y-}3ccR5gW$Gc+i3!0Qr*aib9*e9A%G) z-gebL4g(jDS(sN?BqZ`Xd}-;Q7MR70+_E+V9!E^*j~{J%Uh2k`Xllsc{>FkkPis~F7exQSGpOJ^upA=XRJ)KdQdcV&>7_DOF(=C+COpt2?Jj$i^Dxq0DCUi?(;%P0IO@JvF=k{y*Cf$xQ;ivB(^U_ zGKfdA+*`!QbG$MEvc>folFhLqFW)m`^2_9Am&p~FUsRYjj*MZtb+dZ5uZJrJ<<&cm zSD*frLu}=eU|h33&T#*!N=*5Z7Aa6uF*?G-$ALTotK^THTF^M=OO6FkPakj=DE#pU zY#A#Baa8@>kkl}-$3i1bXk{{720h(an1L4MpsI#z2myC zj)Q006}McXG{d_+fi&8J4f_iijk{mZym_Sx@>;5LEUMLt_gW`u9-WvI*Of(A6L6|l z8{b>(bJY|<$Fz5Bw;p@C*5%yJq9-Riywk|sW$!cBL_^-o^6tFPMixz`WX`T~?5QWG z(%NorfdFO-Fly|v3}d+u##WjpHzziI1!&IsNSGaMl?8*Hmm9P#eO5CPK$~1VEQ;2$ zKSv~;h0Oy5Nso35gF!+5d-Bl+)z*p>@ZT+Ze{|$?Vq;h=%dh|~0#pxZY1}Dl2kVCW z?xLbzi=r@LJcmoQ_T#O~XRjqB=os#9^uB3#a{eyr)EFAEh%JlS*g%6t$d6(>226K% zvm!%K`*6iNybnyLpz!+nm!q7Voa>+8OqaQL9<8mp;}7!_MD3uT268dmhM(`U(nOrQ zvl9?x7O-jqB~*2pyC+wGnBNv(2ser+Was{XNY#S&x5HpQ^M<)|ccH)Pb3k}`>ur~| z=>$Cjs(ToW)!uw#eGrhd^KeaE#<2 zWUPi%QVXCb00~!y?Pwq*F|Kr28m$S-P)SJ5Ay#JpmV?TT77-)Rzb^d;H7Ys7msX(iw-0B%|eMOGkme&UdD|HN)(c8bu~Yz{#QBITXo zKlSTOp{g%`RcMVT1&7Yvl$-e#Y%9{GlU;Ba^{>=vLnY;kk|RKG-Coe?~$MvSpm#!%O*A4%7ochc+e zsB6snGBMJJG^Yp47J72KZ)F))?FLFhWwnm+1T<)Ft+#_GxiILUszZ*T2&F9OjyeAP z6zvkcue4k-%L`R8HuZOme0))X`&%AsQ%Xl`2~t83!0ZGDou^0k6$L|G>Cd?An;T2A z3X!x*g=P}4vRzI>POtUOzPIU}={Uee1&{w3Z*&|ge^b1n3y=jwTpX9a$MNBI34z|> z{ur!l^^=>agA1V243rEo`|;fwKMjNsJd+c=yidA|PYc>loq0w~jIkGi#z9y3CBlPc z?>bN-eEa&A-8Japl{gGCsgT2b>Ig-n_Y(dWu3n1hTCf=z@;pZ$z#Np!kk!_*vkIt* z3ZcYf^1#XwXE-s1w2PuTwVm_`24|+q;ssWvgkaUU)SSNlf#`lFe7&sFbHR6|hAi79 zn~ZGUnb*9cqT<}#7(*1cQDS>o-fYzJQ9S}Gz3Ii=x6D|iq>|fTw9gc`^33unk;kB~ zlPy5&n|KIu9|BXJ+izrAz;OzIYrfDF_xsC063gR5iYqqW#A=Kcz@eH%5=!%m`-T_4U9 z!9Lw(m}?l#{JRk`qF$OdD>p(ceoyRjJ6Nm}hwyMwzHGqF_nA&K1rNIWBs3Jy%079N zFiNOW+2;^&ktQNklN~*}DDMPhLy%E4hHz(mosb0An?**iQLZwGV;d$L4rTxlzs>p# zPxa=2_&I99E8yCD-RfEtobb_1|mb zXo@ssSS%p@NlWB*=CMH8gK3Ja3nX{=k@x&E#e<6#uDlp*h&Q@?6m%pbGAQmshB`#o z)orttEh)~nqVC(Eq8_FVyEjusAkXJUHL<>pSkgBB zYUK0ycs(AIa!q;pki=WQF{-_NGabeoqy8%n%fn)RGq|j{cL%$4?`WGaDtw39E7f02 zAo%P{SGt5UH!t)V#vC1r43#(}049gx{7u}!?z*{MQ_&q3_mP+^_1(kQnZ!gCbIs$- z_(KK&B7I;VrYmS``ruff_Lz6VHz`ov@`Pb>69$ z=PE0&HEOQpaVu%FGqW_gS!2!3ysfYF6xq~a#8?`|kk_%}Z=0qzM_7$dyPromlk!vNX5p%vRg0iTe*e>L{9+-eNO6u`)HxUmdKt zw2&;m5~PzARgMfC?*C|2&`M>HBhImeotf`yRm(`HMtTmTCQ>uGK5}r|Ow-Vabn3c4 zqqDmcAXOw8|a^QN%H?T2e?YyDN;E)mp13ek6T>n$`zL{3wbq~&C@>)YEWI${T2 zGRab$A=sFC+qSX4Jvum4vQWVFExtPTC_UYJX~#OO^SuPypc_S$xRaGD#TTnq+}pH? zi_EZ#E<~hU7A^d$tZe?+4uxx4OFuu${`&PRk>B#sxnA8{%Sq>&Cfq8Vs;FOQe$30` zUiwv|4<9}y8VUFEcVNfHsQtn044w0N3ys78y+bh~wblLG3%(Z#0k`r<)(| zq3CY;Xlh9AzS1i+OUriRx>;68?>l8l${@KIALVIfl_v8+bN0|@ity*|`Ydh^W3{)d zQr8XB`v$Hvsfb$zv_xWA#MDg+xBv;+pIoSq>R0&gSeV=r@!=+Y6c;P2e9ad>hmIjn z{6XjBmX#il!a2FrZI`B!h8&88kE|j@dIhinCoN;;T}g-%$08vO2SgMJGc? z16T`s*{U0sJlIT=#5qX01W+XnnEIqhVq#LYq)7?yU%}r{{<<;Dq|`R^jox&0Qj7NA z40KL=mG7b3N+#T7wN<|49!Q50KRZV!^_$Zr<- zx%~ZT)X?V^ltV7n_0;?x9CN|R_&zkM$VnFWCRFbrIW7K^@UD2K`#Pzq=)(K~=n`Ml z|8=P9ROpUQSD+E>>E01nGZmf88BYZn^Rg;VdaLY!$d5s%NS(%}vX@nxW(M5SF_UT` zf~1FoQa?P876%>9<0`ckLWPe{s2ra5?mQbiDY}QA<)x&e8tZAX6Qvz^c7!#_=^pZ20O_OahVdB{9<%qqJ~+8FR%X7xwWROomk5QvG**?% z9DGt53sOdfP?ogY`D5AI(t^n|6vFP`0+buOy<&;ez8A@(rgV%@OwhtiNA>DL8S2&(D8kTWg|%A0y+4WGhE>)g{2vYqYP&4_Xu&IS-~4$wd# zuS@1Nch)Tbs6=wPeXyHJ7S8D&*bICuDM5M6c4{fGr-SB(y~?H@b?ApWpGC}DqSL*? zanaHEaTP%?Vbq(c-e0~tN%4t$dU~^0mP{fWN*EEG99rzJKE%XCbMo@m;*wtR-Nlvy zZ#G>kO}Veo+&SyOi+pdpOW(_@R4=&P2&C4uM=I9+Cc+u+&$LtLWWE;tya*SXy1djhc$fVr<5ilxTZ56^fxd2KApZcz=e535e7E8K0v5|rgXH2aiL zR^oDZ`#P6veks$-DN~&HySGv|R}XyJ{nZ$M^E`HU);?uvIO<_lTK0-#HSt@~_P4S6 zlATz z8^**`6Fm*}b&@=JVx+n7 zcu|z$ub;+#rreix7%$w<<#Dyann_pLCE7bhWS5l4gGI=a=auL?a-~jC_%m~-&62QY z*}IRwIqAJ6%T${^P36-tU42qMDp}67kDaiv;A+Fq{zz08Jj7f2Bu`craJN-1Z-#d% zQ-~PHdya@QM?bw5{j{g2XDFc{a0{HCa%N^`7HFn_mFByOf;5$1$)MM8c1DK0hQwy^ z*2-9+!L93T^n+ie9~WEH2TadS<7!|nfV`-W#tvna12ScM6H*QhJF5UHO5ime;3_=G ziswC4v9Y#Bx4MAbKQ_IMl(cnzq$=>VpiAWNmX$r$YyaK%NjJmz{#w=1@N# zq~mKV8j9az4YbqwJ~MN#=3tG>sMOKWIQUK1aUBP}R^IIU{g16bB9~(Dm)>OD^);gB zBGrP4L`C;Nc$&G(M-2^#0-CjDVLc_(A*IW8E4BO9*b;LDs};A0Q!Er|`4`GB^uwe{ZSKY{bS3lyCQUInHTA$DgscTSTKf5e7W2v#%!oM&;sJdaqDPwblB!yA z6MBT#L|<7YVL7N-_nz16=?jhg!d5?$hH7jJ=lM+LmJ6S&Q$!3IRV_2Cy+LO5dVf8M z_>FxaG=H9$MM;JJrbtTjPHPfowPU=p=ape*=Gcg*!#voB)pFtLZ{2BKM6Y^s9i$xUANf`W zR7gY12#z2^-F4^BMW^9%C6#z?Jzy(%lbwch2uHX-=Ugd4k4?tM#GGZ4h@QMtvD-ry zWN&vzX^`p*p!OYBW8`y&%9V^uHOYEnlSJKQ3hRzSfwb`fWM7u)&m#~LmrqPxHavmb zSK;9a`l+o=&XmsPHlgkOej{!6Wa5p%x}|oa^H)+%FV|`0>IG(#Ud^fWr};y9F#gA~ zW>2cnZm8HUQP5ZWp96}JWHi?*$Mmn5MJxoHyQ6Wb3nE*MH?>pityb^LN(JzyaKvS{ zJBykDYOA}dEjYJ9`V`7g%=pclHzN`vJD?k&i(fu?`i(^#W>n!e7&oq1;zb~mLKL1P zrOa=;)f_!9xA&?rvvpqP2=pj73)|UvDeP4GF)aD)PSN7LMuq$Ivv|Jaq|W!^xzEV z$mN`pjC_|LPJKdYS+1roALA_V;HZ1D1$DfSD?UCWjE@l?ovM7^G-n^I7BP@#mU~Ay z6^JFAJalP`?`R2iw5{F5JG>(&*~!D_=li*kuD?^jn9;gwak5w1L{wb3ae$+iH~CJ8c#;2(_8E@w2qOlA+ed(1j9FT}||i${5ec zl&PumN8hx?ma4zY?q^G0>~4u=vmrd$+TN}S>!t-id+(H&k-mqiK75t>jg}%e%(PTI zz5qBS7p(I>miz1sIvbopAPN)_I2qfhDx|(Y&x4c&!}wS4c_}0KUttJ42&=S(Y5Ly? z*?NvQ5SrHbp)mJfjWS8{pOe*NOaVs6w6$$+O3H1R$wwr(5eR2vf(r=vS9>Z*a=0Pgom#;|NpN4 z4}MMS$Iq2$P{av+k#G;_*%&C@Q&U55!t*nT6Jd4;{OyCkXK3IDfjAWtiZ~6Ahiz0LV4J@ z%epwhd!hgN#)>D6Mq!J;r(kDgg|u>U^>w$iy^lu7{_7c|w5zM54Wp8?6)!g<{>vjB zr1=Q@P~qPdzmK(zhn2gXD;j@vvO>bjH^qEhERZR%$rb!2K2EO4|J_OOQ4#(7ZT`K@ of1bg!Bce%oo4;p?-{gNj{Qthty}yeBU@L@@yt-V0jK!n>0YA~n-v9sr literal 0 HcmV?d00001 diff --git a/docs/src/components/showcase-sites.astro b/docs/src/components/showcase-sites.astro index 4cf5b6079ba..75ada4ea703 100644 --- a/docs/src/components/showcase-sites.astro +++ b/docs/src/components/showcase-sites.astro @@ -174,4 +174,5 @@ import FluidGrid from './fluid-grid.astro'; href="https://docs.ryzekit.com/" thumbnail="docs.ryzekit.com.png" /> + From bf939947a3b219a75cd7ac8ade27694c8a927937 Mon Sep 17 00:00:00 2001 From: Chris Swithinbank Date: Mon, 16 Sep 2024 16:33:17 +0200 Subject: [PATCH 013/493] Re-enable skipped navigation test (#2329) --- .../build-format-file/{navigation.ts => navigation.test.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename packages/starlight/__tests__/build-format-file/{navigation.ts => navigation.test.ts} (97%) diff --git a/packages/starlight/__tests__/build-format-file/navigation.ts b/packages/starlight/__tests__/build-format-file/navigation.test.ts similarity index 97% rename from packages/starlight/__tests__/build-format-file/navigation.ts rename to packages/starlight/__tests__/build-format-file/navigation.test.ts index f733049acef..f91e98a355c 100644 --- a/packages/starlight/__tests__/build-format-file/navigation.ts +++ b/packages/starlight/__tests__/build-format-file/navigation.test.ts @@ -17,7 +17,7 @@ vi.mock('astro:content', async () => describe('getSidebar with build.format = "file"', () => { test('returns an array of sidebar entries with its file extension', () => { - expect(getSidebar('/', undefined)).toMatchInlineSnapshot(` + expect(getSidebar('/index.html', undefined)).toMatchInlineSnapshot(` [ { "attrs": {}, From d7a295e5f63171c7eee9fc11333157d8c7e6c803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Matos?= Date: Wed, 18 Sep 2024 08:43:29 +0100 Subject: [PATCH 014/493] Fix broken restoration of remark directives. (#2327) Co-authored-by: Chris Swithinbank --- .changeset/gold-coats-destroy.md | 5 +++ .../__tests__/remark-rehype/asides.test.ts | 32 +++++++++++++++++++ packages/starlight/integrations/asides.ts | 3 +- 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 .changeset/gold-coats-destroy.md diff --git a/.changeset/gold-coats-destroy.md b/.changeset/gold-coats-destroy.md new file mode 100644 index 00000000000..416391d637d --- /dev/null +++ b/.changeset/gold-coats-destroy.md @@ -0,0 +1,5 @@ +--- +"@astrojs/starlight": patch +--- + +Fixes restoration of remark directives for nodes with custom data attached. diff --git a/packages/starlight/__tests__/remark-rehype/asides.test.ts b/packages/starlight/__tests__/remark-rehype/asides.test.ts index 88110f682ae..cda21bb7b5f 100644 --- a/packages/starlight/__tests__/remark-rehype/asides.test.ts +++ b/packages/starlight/__tests__/remark-rehype/asides.test.ts @@ -270,3 +270,35 @@ test('lets remark plugin injected by Starlight plugins handle text and leaf dire "

This is a:test of a sentence with a TEXT FROM REMARK PLUGIN directive handled by another remark plugin and some other text:name[content]{key="val"} directives not handled by any plugin.

" `); }); + +test('does not transform back directive nodes with data', async () => { + const processor = await createMarkdownProcessor({ + remarkPlugins: [ + ...starlightAsides({ + starlightConfig, + astroConfig: { + root: new URL(import.meta.url), + srcDir: new URL('./_src/', import.meta.url), + }, + useTranslations, + }), + // A custom remark plugin updating the node with data that should be consumed by rehype. + function customRemarkPlugin() { + return function transformer(tree: Root) { + visit(tree, (node) => { + if (node.type !== 'textDirective') return; + node.data ??= {}; + node.data.hName = 'span'; + node.data.hProperties = { class: `api` }; + }); + }; + }, + remarkDirectivesRestoration, + ], + }); + + const res = await processor.render(`This method is available in the :api[thing] API.`); + expect(res.code).toMatchInlineSnapshot( + `"

This method is available in the thing API.

"` + ); +}); diff --git a/packages/starlight/integrations/asides.ts b/packages/starlight/integrations/asides.ts index d98e6a403ae..7c4dfb74f24 100644 --- a/packages/starlight/integrations/asides.ts +++ b/packages/starlight/integrations/asides.ts @@ -227,7 +227,8 @@ export function remarkDirectivesRestoration() { if ( index !== undefined && parent && - (node.type === 'textDirective' || node.type === 'leafDirective') + (node.type === 'textDirective' || node.type === 'leafDirective') && + node.data === undefined ) { transformUnhandledDirective(node, index, parent); return; From 5269aad928773ae08b35ba8e19c0f2832d0d2c89 Mon Sep 17 00:00:00 2001 From: HiDeoo <494699+HiDeoo@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:55:37 +0200 Subject: [PATCH 015/493] Use `i18next` for UI strings and add new `injectTranslations` plugin callback (#1923) Co-authored-by: Chris Swithinbank <357379+delucis@users.noreply.github.com> Co-authored-by: Chris Swithinbank Co-authored-by: HiDeoo <494699+HiDeoo@users.noreply.github.com> --- .changeset/cool-experts-sort.md | 19 +++ .changeset/eighty-beds-attack.md | 11 ++ .changeset/thirty-dodos-drop.md | 11 ++ .npmrc | 1 + docs/src/content/docs/guides/i18n.mdx | 112 +++++++++++++ docs/src/content/docs/reference/overrides.md | 6 - docs/src/content/docs/reference/plugins.md | 69 ++++++++ package.json | 10 +- packages/docsearch/DocSearch.astro | 19 ++- packages/docsearch/package.json | 5 +- packages/markdoc/package.json | 2 + .../__tests__/basics/route-data.test.ts | 19 ++- .../basics/starlight-page-route-data.test.ts | 11 +- .../__tests__/basics/translations.test.ts | 149 ++++++++++++++++++ .../route-data.test.ts | 19 --- .../route-data.test.ts | 19 --- .../__tests__/i18n/route-data.test.ts | 32 ---- .../__tests__/i18n/translations-fs.test.ts | 28 ++++ .../__tests__/i18n/translations.test.ts | 8 + .../__tests__/plugins/translations.test.ts | 48 ++++++ .../__tests__/plugins/vitest.config.ts | 22 ++- packages/starlight/__tests__/test-config.ts | 23 ++- packages/starlight/__tests__/test-utils.ts | 13 +- .../components/DraftContentNotice.astro | 4 +- packages/starlight/components/EditLink.astro | 4 +- .../components/FallbackContentNotice.astro | 4 +- packages/starlight/components/Footer.astro | 2 +- .../starlight/components/LanguageSelect.astro | 4 +- .../starlight/components/LastUpdated.astro | 4 +- .../components/MobileMenuToggle.astro | 4 +- .../components/MobileTableOfContents.astro | 4 +- packages/starlight/components/PageFrame.astro | 4 +- .../starlight/components/Pagination.astro | 6 +- packages/starlight/components/Search.astro | 18 +-- .../components/SidebarRestorePoint.astro | 2 +- packages/starlight/components/SkipLink.astro | 4 +- .../components/TableOfContents.astro | 4 +- .../starlight/components/ThemeSelect.astro | 10 +- packages/starlight/i18n.d.ts | 18 +++ packages/starlight/index.ts | 36 ++++- packages/starlight/integrations/asides.ts | 2 +- .../integrations/virtual-user-config.ts | 5 +- packages/starlight/locals.d.ts | 17 ++ packages/starlight/locals.ts | 8 + packages/starlight/package.json | 4 +- .../utils/createTranslationSystem.ts | 93 ++++++++--- packages/starlight/utils/i18n.ts | 20 +++ packages/starlight/utils/plugins.ts | 69 +++++++- packages/starlight/utils/route-data.ts | 9 +- packages/starlight/utils/starlight-page.ts | 21 +-- packages/starlight/utils/translations-fs.ts | 7 +- packages/starlight/utils/translations.ts | 13 +- packages/starlight/utils/types.ts | 15 ++ packages/starlight/virtual.d.ts | 5 + packages/starlight/vitest.config.ts | 8 +- packages/tailwind/package.json | 1 + pnpm-lock.yaml | 121 +++++++------- tsconfig.json | 1 - 58 files changed, 925 insertions(+), 282 deletions(-) create mode 100644 .changeset/cool-experts-sort.md create mode 100644 .changeset/eighty-beds-attack.md create mode 100644 .changeset/thirty-dodos-drop.md create mode 100644 packages/starlight/__tests__/basics/translations.test.ts delete mode 100644 packages/starlight/__tests__/i18n-non-root-single-locale/route-data.test.ts delete mode 100644 packages/starlight/__tests__/i18n-single-root-locale/route-data.test.ts delete mode 100644 packages/starlight/__tests__/i18n/route-data.test.ts create mode 100644 packages/starlight/__tests__/plugins/translations.test.ts create mode 100644 packages/starlight/i18n.d.ts create mode 100644 packages/starlight/locals.d.ts create mode 100644 packages/starlight/locals.ts create mode 100644 packages/starlight/utils/types.ts diff --git a/.changeset/cool-experts-sort.md b/.changeset/cool-experts-sort.md new file mode 100644 index 00000000000..6de9fd8c02f --- /dev/null +++ b/.changeset/cool-experts-sort.md @@ -0,0 +1,19 @@ +--- +'@astrojs/starlight': minor +--- + +Overhauls the built-in localization system which is now powered by the [`i18next`](https://www.i18next.com/) library and available to use anywhere in your documentation website. + +See the [“Using UI translations”](https://starlight.astro.build/guides/i18n/#using-ui-translations) guide to learn more about how to access built-in UI labels or your own custom strings in your project. Plugin authors can also use the new [`injectTranslations()`](https://starlight.astro.build/reference/plugins/#injecttranslations) helper to add or update translation strings. + +⚠️ **BREAKING CHANGE:** The `Astro.props.labels` props has been removed from the props passed down to custom component overrides. + +If you are relying on `Astro.props.labels` (for example to read a built-in UI label), you will need to update your code to use the new [`Astro.locals.t()`](https://starlight.astro.build/guides/i18n/#using-ui-translations) helper instead. + +```astro +--- +import type { Props } from '@astrojs/starlight/props'; +// The `search.label` UI label for this page’s language: +const searchLabel = Astro.locals.t('search.label'); +--- +``` diff --git a/.changeset/eighty-beds-attack.md b/.changeset/eighty-beds-attack.md new file mode 100644 index 00000000000..000fa3e945c --- /dev/null +++ b/.changeset/eighty-beds-attack.md @@ -0,0 +1,11 @@ +--- +'@astrojs/starlight-docsearch': minor +--- + +⚠️ **BREAKING CHANGE:** The minimum supported version of Starlight is now 0.28.0 + +Please use the `@astrojs/upgrade` command to upgrade your project: + +```sh +npx @astrojs/upgrade +``` diff --git a/.changeset/thirty-dodos-drop.md b/.changeset/thirty-dodos-drop.md new file mode 100644 index 00000000000..4fef44db65a --- /dev/null +++ b/.changeset/thirty-dodos-drop.md @@ -0,0 +1,11 @@ +--- +'@astrojs/starlight': minor +--- + +⚠️ **BREAKING CHANGE:** The minimum supported version of Astro is now 4.14.0 + +Please update Astro and Starlight together: + +```sh +npx @astrojs/upgrade +``` diff --git a/.npmrc b/.npmrc index 901b4e86a04..11cb30ee198 100644 --- a/.npmrc +++ b/.npmrc @@ -1,3 +1,4 @@ prefer-workspace-packages=true link-workspace-packages=true shell-emulator=true +auto-install-peers=false diff --git a/docs/src/content/docs/guides/i18n.mdx b/docs/src/content/docs/guides/i18n.mdx index 2fe235b2b6a..a320fbab7af 100644 --- a/docs/src/content/docs/guides/i18n.mdx +++ b/docs/src/content/docs/guides/i18n.mdx @@ -276,6 +276,118 @@ export const collections = { Learn more about content collection schemas in [“Defining a collection schema”](https://docs.astro.build/en/guides/content-collections/#defining-a-collection-schema) in the Astro docs. +## Using UI translations + +You can access Starlight’s [built-in UI strings](/guides/i18n/#translate-starlights-ui) as well as [user-defined](/guides/i18n/#extend-translation-schema), and [plugin-provided](/reference/plugins/#injecttranslations) UI strings through a unified API powered by [i18next](https://www.i18next.com/). +This includes support for features like [interpolation](https://www.i18next.com/translation-function/interpolation) and [pluralization](https://www.i18next.com/translation-function/plurals). + +In Astro components, this API is available as part of the [global `Astro` object](https://docs.astro.build/en/reference/api-reference/#astrolocals) as `Astro.locals.t`: + +```astro title="example.astro" +

+ {Astro.locals.t('404.text')} +

+``` + +You can also use the API in [endpoints](https://docs.astro.build/en/guides/endpoints/), where the `locals` object is available as part of the [endpoint context](https://docs.astro.build/en/reference/api-reference/#contextlocals): + +```ts title="src/pages/404.ts" +export const GET = (context) => { + return new Response(context.locals.t('404.text')); +}; +``` + +### Rendering a UI string + +Render UI strings using the `locals.t()` function. +This is an instance of i18next’s `t()` function, which takes a UI string key as its first argument and returns the corresponding translation for the current language. + +For example, given a custom translation file with the following content: + +```json title="src/content/i18n/en.json" +{ + "link.astro": "Astro documentation", + "link.astro.custom": "Astro documentation for {{feature}}" +} +``` + +The first UI string can be rendered by passing `'link.astro'` to the `t()` function: + +```astro {3} + + + {Astro.locals.t('link.astro')} + + +``` + +The second UI string uses i18next’s [interpolation syntax](https://www.i18next.com/translation-function/interpolation) for the `{{feature}}` placeholder. +The value for `feature` must be set in an options object passed as the second argument to `t()`: + +```astro {3} + + + {Astro.locals.t('link.astro.custom', { feature: 'Astro DB' })} + + +``` + +See the [i18next documentation](https://www.i18next.com/overview/api#t) for more information on how to use the `t()` function with interpolation, formatting, and more. + +### Advanced APIs + +#### `t.all()` + +The `locals.t.all()` function returns an object containing all UI strings available for the current locale. + +```astro +--- +// src/components/Example.astro +const allStrings = Astro.locals.t.all(); +// ^ +// { +// "skipLink.label": "Skip to content", +// "search.label": "Search", +// … +// } +--- +``` + +#### `t.exists()` + +To check if a translation key exists for a locale, use the `locals.t.exists()` function with the translation key as first argument. +Pass an optional second argument if you need to override the current locale. + +```astro +--- +// src/components/Example.astro +const keyExistsInCurrentLocale = Astro.locals.t.exists('a.key'); +// ^ true +const keyExistsInFrench = Astro.locals.t.exists('another.key', { lng: 'fr' }); +// ^ false +--- +``` + +See the [`exists()` reference in the i18next documentation](https://www.i18next.com/overview/api#exists) for more information. + +#### `t.dir()` + +The `locals.t.dir()` function returns the text direction of the current or a specific locale. + +```astro +--- +// src/components/Example.astro +const currentDirection = Astro.locals.t.dir(); +// ^ +// 'ltr' +const arabicDirection = Astro.locals.t.dir('ar'); +// ^ +// 'rtl' +--- +``` + +See the [`dir()` reference in the i18next documentation](https://www.i18next.com/overview/api#dir) for more information. + ## Accessing the current locale You can use [`Astro.currentLocale`](https://docs.astro.build/en/reference/api-reference/#astrocurrentlocale) to read the current locale in `.astro` components. diff --git a/docs/src/content/docs/reference/overrides.md b/docs/src/content/docs/reference/overrides.md index 9293654be2a..8e5ed0124cc 100644 --- a/docs/src/content/docs/reference/overrides.md +++ b/docs/src/content/docs/reference/overrides.md @@ -148,12 +148,6 @@ JavaScript `Date` object representing when this page was last updated if enabled `URL` object for the address where this page can be edited if enabled. -#### `labels` - -**Type:** `Record` - -An object containing UI strings localized for the current page. See the [“Translate Starlight’s UI”](/guides/i18n/#translate-starlights-ui) guide for a list of all the available keys. - --- ## Components diff --git a/docs/src/content/docs/reference/plugins.md b/docs/src/content/docs/reference/plugins.md index 7f44f60b6a0..e22d146a3b9 100644 --- a/docs/src/content/docs/reference/plugins.md +++ b/docs/src/content/docs/reference/plugins.md @@ -27,6 +27,7 @@ interface StarlightPlugin { command: 'dev' | 'build' | 'preview'; isRestart: boolean; logger: AstroIntegrationLogger; + injectTranslations: (Record>) => void; }) => void | Promise; }; } @@ -161,3 +162,71 @@ The example above will log a message that includes the provided info message: ```shell [long-process-plugin] Starting long process… ``` + +#### `injectTranslations` + +**type:** `(translations: Record>) => void` + +A callback function to add or update translation strings used in Starlight’s [localization APIs](/guides/i18n/#using-ui-translations). + +In the following example, a plugin injects translations for a custom UI string named `myPlugin.doThing` for the `en` and `fr` locales: + +```ts {6-13} /(injectTranslations)[^(]/ +// plugin.ts +export default { + name: 'plugin-with-translations', + hooks: { + setup({ injectTranslations }) { + injectTranslations({ + en: { + 'myPlugin.doThing': 'Do the thing', + }, + fr: { + 'myPlugin.doThing': 'Faire le truc', + }, + }); + }, + }, +}; +``` + +To use the injected translations in your plugin UI, follow the [“Using UI translations” guide](/guides/i18n/#using-ui-translations). + +Types for a plugin’s injected translation strings are generated automatically in a user’s project, but are not yet available when working in your plugin’s codebase. +To type the `locals.t` object in the context of your plugin, declare the following global namespaces in a TypeScript declaration file: + +```ts +// env.d.ts +declare namespace App { + type StarlightLocals = import('@astrojs/starlight').StarlightLocals; + // Define the `locals.t` object in the context of a plugin. + interface Locals extends StarlightLocals {} +} + +declare namespace StarlightApp { + // Define the additional plugin translations in the `I18n` interface. + interface I18n { + 'myPlugin.doThing': string; + } +} +``` + +You can also infer the types for the `StarlightApp.I18n` interface from a source file if you have an object containing your translations. + +For example, given the following source file: + +```ts title="ui-strings.ts" +export const UIStrings = { + en: { 'myPlugin.doThing': 'Do the thing' }, + fr: { 'myPlugin.doThing': 'Faire le truc' }, +}; +``` + +The following declaration would infer types from the English keys in the source file: + +```ts title="env.d.ts" +declare namespace StarlightApp { + type UIStrings = typeof import('./ui-strings').UIStrings.en; + interface I18n extends UIStrings {} +} +``` diff --git a/package.json b/package.json index e328ac87f59..41c2ad50b0d 100644 --- a/package.json +++ b/package.json @@ -42,5 +42,13 @@ "limit": "14.5 kB", "gzip": true } - ] + ], + "pnpm": { + "peerDependencyRules": { + "ignoreMissing": [ + "@algolia/client-search", + "search-insights" + ] + } + } } diff --git a/packages/docsearch/DocSearch.astro b/packages/docsearch/DocSearch.astro index 12db6b465bf..467192afee5 100644 --- a/packages/docsearch/DocSearch.astro +++ b/packages/docsearch/DocSearch.astro @@ -4,8 +4,6 @@ import '@docsearch/css/dist/modal.css'; import type docsearch from '@docsearch/js'; import './variables.css'; -const { labels } = Astro.props; - type DocSearchTranslationProps = Pick< Parameters[0], 'placeholder' | 'translations' @@ -13,15 +11,18 @@ type DocSearchTranslationProps = Pick< const pick = (keyStart: string) => Object.fromEntries( - Object.entries(labels) + Object.entries(Astro.locals.t.all()) .filter(([key]) => key.startsWith(keyStart)) .map(([key, value]) => [key.replace(keyStart, ''), value]) ); const docsearchTranslations: DocSearchTranslationProps = { - placeholder: labels['search.label'], + placeholder: Astro.locals.t('search.label'), translations: { - button: { buttonText: labels['search.label'], buttonAriaLabel: labels['search.label'] }, + button: { + buttonText: Astro.locals.t('search.label'), + buttonAriaLabel: Astro.locals.t('search.label'), + }, modal: { searchBox: pick('docsearch.searchBox.'), startScreen: pick('docsearch.startScreen.'), @@ -34,7 +35,11 @@ const docsearchTranslations: DocSearchTranslationProps = { --- - diff --git a/packages/docsearch/package.json b/packages/docsearch/package.json index d4ec221e6d0..8826ac25aa6 100644 --- a/packages/docsearch/package.json +++ b/packages/docsearch/package.json @@ -25,10 +25,13 @@ "./schema": "./schema.ts" }, "peerDependencies": { - "@astrojs/starlight": ">=0.14.0" + "@astrojs/starlight": ">=0.28.0" }, "dependencies": { "@docsearch/css": "^3.6.0", "@docsearch/js": "^3.6.0" + }, + "devDependencies": { + "@astrojs/starlight": "workspace:*" } } diff --git a/packages/markdoc/package.json b/packages/markdoc/package.json index 81e79cd8249..8b5898c46b7 100644 --- a/packages/markdoc/package.json +++ b/packages/markdoc/package.json @@ -17,6 +17,8 @@ "./components": "./components.ts" }, "devDependencies": { + "@astrojs/markdoc": "^0.11.4", + "@astrojs/starlight": "workspace:*", "vitest": "^1.6.0" }, "peerDependencies": { diff --git a/packages/starlight/__tests__/basics/route-data.test.ts b/packages/starlight/__tests__/basics/route-data.test.ts index d59ce0135e2..f3293259b4b 100644 --- a/packages/starlight/__tests__/basics/route-data.test.ts +++ b/packages/starlight/__tests__/basics/route-data.test.ts @@ -1,6 +1,7 @@ import { expect, test, vi } from 'vitest'; import { generateRouteData } from '../../utils/route-data'; import { routes } from '../../utils/routing'; +import pkg from '../../package.json'; vi.mock('astro:content', async () => (await import('../test-utils')).mockedAstroContent({ @@ -87,12 +88,24 @@ test('uses explicit last updated date from frontmatter', () => { expect(data.lastUpdated).toEqual(route.entry.data.lastUpdated); }); -test('includes localized labels', () => { +test('throws when accessing a label using the deprecated `labels` prop in pre v1 versions', () => { + const isPreV1 = pkg.version[0] === '0'; + const route = routes[0]!; const data = generateRouteData({ props: { ...route, headings: [{ depth: 1, slug: 'heading-1', text: 'Heading 1' }] }, url: new URL('https://example.com'), }); - expect(data.labels).toBeDefined(); - expect(data.labels['skipLink.label']).toBe('Skip to content'); + + if (isPreV1) { + expect(() => data.labels['any']).toThrowErrorMatchingInlineSnapshot(` + "[AstroUserError]: + The \`labels\` prop in component overrides has been removed. + Hint: + Replace \`Astro.props.labels["any"]\` with \`Astro.locals.t("any")\` instead. + For more information see https://starlight.astro.build/guides/i18n/#using-ui-translations" + `); + } else { + expect(() => data.labels['any']).not.toThrow(); + } }); diff --git a/packages/starlight/__tests__/basics/starlight-page-route-data.test.ts b/packages/starlight/__tests__/basics/starlight-page-route-data.test.ts index 63ac6efd38a..c3510a65c4b 100644 --- a/packages/starlight/__tests__/basics/starlight-page-route-data.test.ts +++ b/packages/starlight/__tests__/basics/starlight-page-route-data.test.ts @@ -467,13 +467,18 @@ test('hides the sidebar if the `hasSidebar` option is not specified and the spla expect(data.hasSidebar).toBe(false); }); -test('includes localized labels', async () => { +test('throws when accessing a label using the deprecated `labels` prop', async () => { const data = await generateStarlightPageRouteData({ props: starlightPageProps, url: starlightPageUrl, }); - expect(data.labels).toBeDefined(); - expect(data.labels['skipLink.label']).toBe('Skip to content'); + expect(() => data.labels['any']).toThrowErrorMatchingInlineSnapshot(` + "[AstroUserError]: + The \`labels\` prop in component overrides has been removed. + Hint: + Replace \`Astro.props.labels["any"]\` with \`Astro.locals.t("any")\` instead. + For more information see https://starlight.astro.build/guides/i18n/#using-ui-translations" + `); }); test('uses provided edit URL if any', async () => { diff --git a/packages/starlight/__tests__/basics/translations.test.ts b/packages/starlight/__tests__/basics/translations.test.ts new file mode 100644 index 00000000000..abada3e4101 --- /dev/null +++ b/packages/starlight/__tests__/basics/translations.test.ts @@ -0,0 +1,149 @@ +import { describe, expect, test, vi } from 'vitest'; +import { useTranslations } from '../../utils/translations'; +import translations from '../../translations'; + +describe('useTranslations()', () => { + test('includes localized UI strings', () => { + const t = useTranslations(undefined); + expect(t).toBeTypeOf('function'); + expect(t('skipLink.label')).toBe('Skip to content'); + }); +}); + +describe('t()', async () => { + // The mocked user-defined translations are scoped to this `describe` block so that they do not + // affect other tests (`vi.mock` → `vi.doMock`). + vi.doMock('astro:content', async () => + (await import('../test-utils')).mockedAstroContent({ + i18n: [ + [ + 'en', + { + 'test.interpolation': '{{subject}} is {{adjective}}', + 'test.dataModel': 'Powered by {{integration.name}}', + 'test.escape': 'The tag is {{tag}}', + 'test.unescape': 'The tag is {{- tag}}', + 'test.currency': 'The price is {{price, currency(USD)}}', + 'test.list': '{{subjects, list}} are awesome', + 'test.count_one': '{{count}} project', + 'test.count_other': '{{count}} projects', + 'test.nesting1': '$t(test.nesting2) is nested', + 'test.nesting2': 'this UI string', + }, + // We do not strip unknown translations in this test so that user-defined translations can + // override plugin translations like it would in a real- world scenario were the plugin + // would have provided a custom schema to extend the translations. + { stripUnknown: false }, + ], + ], + }) + ); + // Reset the modules registry so that re-importing `../../utils/translations` re-evaluates the + // module and re-computes `useTranslations`. Re-importing the module is necessary because + // top-level imports cannot be re-evaluated. + vi.resetModules(); + const { useTranslations } = await import('../../utils/translations'); + const t = useTranslations(undefined); + + test('supports using interpolation', () => { + expect(t).toBeTypeOf('function'); + // @ts-expect-error - using a mocked translation key. + expect(t('test.interpolation', { subject: 'Starlight', adjective: 'amazing' })).toBe( + 'Starlight is amazing' + ); + }); + + test('supports using data models', () => { + expect(t).toBeTypeOf('function'); + // @ts-expect-error - using a mocked translation key. + expect(t('test.dataModel', { integration: { name: 'Starlight' } })).toBe( + 'Powered by Starlight' + ); + }); + + test('escapes by default', () => { + expect(t).toBeTypeOf('function'); + // @ts-expect-error - using a mocked translation key. + expect(t('test.escape', { tag: '' })).toBe('The tag is <img />'); + }); + + test('supports unescaped strings', () => { + expect(t).toBeTypeOf('function'); + // @ts-expect-error - using a mocked translation key. + expect(t('test.unescape', { tag: '' })).toBe('The tag is '); + }); + + test('supports currencies', () => { + expect(t).toBeTypeOf('function'); + // @ts-expect-error - using a mocked translation key. + expect(t('test.currency', { price: 1000 })).toBe('The price is $1,000.00'); + }); + + test('supports lists', () => { + expect(t).toBeTypeOf('function'); + // @ts-expect-error - using a mocked translation key. + expect(t('test.list', { subjects: ['Astro', 'Starlight', 'Astro DB'] })).toBe( + 'Astro, Starlight, and Astro DB are awesome' + ); + }); + + test('supports counts', () => { + expect(t).toBeTypeOf('function'); + // @ts-expect-error - using a mocked translation key. + expect(t('test.count', { count: 1 })).toBe('1 project'); + // @ts-expect-error - using a mocked translation key. + expect(t('test.count', { count: 20 })).toBe('20 projects'); + }); + + test('supports nesting', () => { + expect(t).toBeTypeOf('function'); + // @ts-expect-error - using a mocked translation key. + expect(t('test.nesting1')).toBe('this UI string is nested'); + }); + + test('returns the UI string key if the translation is missing', () => { + expect(t).toBeTypeOf('function'); + // @ts-expect-error - using a missing translation key. + expect(t('test.unknown')).toBe('test.unknown'); + }); +}); + +describe('t.all()', async () => { + // See the `t()` tests for an explanation of how the user-defined translations are mocked. + vi.doMock('astro:content', async () => + (await import('../test-utils')).mockedAstroContent({ + i18n: [['en', { 'test.foo': 'bar' }, { stripUnknown: false }]], + }) + ); + vi.resetModules(); + const { useTranslations } = await import('../../utils/translations'); + const t = useTranslations(undefined); + + test('returns all translations including custom ones', () => { + expect(t.all).toBeTypeOf('function'); + expect(t.all()).toEqual({ ...translations.en, 'test.foo': 'bar' }); + }); +}); + +describe('t.exists()', async () => { + // See the `t()` tests for an explanation of how the user-defined translations are mocked. + vi.doMock('astro:content', async () => + (await import('../test-utils')).mockedAstroContent({ + i18n: [['en', { 'test.foo': 'bar' }, { stripUnknown: false }]], + }) + ); + vi.resetModules(); + const { useTranslations } = await import('../../utils/translations'); + const t = useTranslations(undefined); + + test('returns `true` for existing translations', () => { + expect(t.exists).toBeTypeOf('function'); + expect(t.exists('skipLink.label')).toBe(true); + expect(t.exists('test.foo')).toBe(true); + }); + + test('returns `false` for unknown translations', () => { + expect(t.exists).toBeTypeOf('function'); + expect(t.exists('test.unknown')).toBe(false); + }); +}); diff --git a/packages/starlight/__tests__/i18n-non-root-single-locale/route-data.test.ts b/packages/starlight/__tests__/i18n-non-root-single-locale/route-data.test.ts deleted file mode 100644 index dbf5d354816..00000000000 --- a/packages/starlight/__tests__/i18n-non-root-single-locale/route-data.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { expect, test, vi } from 'vitest'; -import { generateRouteData } from '../../utils/route-data'; -import { routes } from '../../utils/routing'; - -vi.mock('astro:content', async () => - (await import('../test-utils')).mockedAstroContent({ - docs: [['fr/index.mdx', { title: 'Accueil' }]], - }) -); - -test('includes localized labels (fr)', () => { - const route = routes[0]!; - const data = generateRouteData({ - props: { ...route, headings: [{ depth: 1, slug: 'heading-1', text: 'Heading 1' }] }, - url: new URL('https://example.com'), - }); - expect(data.labels).toBeDefined(); - expect(data.labels['skipLink.label']).toBe('Aller au contenu'); -}); diff --git a/packages/starlight/__tests__/i18n-single-root-locale/route-data.test.ts b/packages/starlight/__tests__/i18n-single-root-locale/route-data.test.ts deleted file mode 100644 index 654ba1b7467..00000000000 --- a/packages/starlight/__tests__/i18n-single-root-locale/route-data.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { expect, test, vi } from 'vitest'; -import { generateRouteData } from '../../utils/route-data'; -import { routes } from '../../utils/routing'; - -vi.mock('astro:content', async () => - (await import('../test-utils')).mockedAstroContent({ - docs: [['index.mdx', { title: 'Accueil' }]], - }) -); - -test('includes localized labels (fr)', () => { - const route = routes[0]!; - const data = generateRouteData({ - props: { ...route, headings: [{ depth: 1, slug: 'heading-1', text: 'Heading 1' }] }, - url: new URL('https://example.com'), - }); - expect(data.labels).toBeDefined(); - expect(data.labels['skipLink.label']).toBe('Aller au contenu'); -}); diff --git a/packages/starlight/__tests__/i18n/route-data.test.ts b/packages/starlight/__tests__/i18n/route-data.test.ts deleted file mode 100644 index 57beed0c1d8..00000000000 --- a/packages/starlight/__tests__/i18n/route-data.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { expect, test, vi } from 'vitest'; -import { generateRouteData } from '../../utils/route-data'; -import { routes } from '../../utils/routing'; - -vi.mock('astro:content', async () => - (await import('../test-utils')).mockedAstroContent({ - docs: [ - ['fr/index.mdx', { title: 'Accueil' }], - ['pt-br/index.mdx', { title: 'Pagina inicial' }], - ], - }) -); - -test('includes localized labels (fr)', () => { - const route = routes[0]!; - const data = generateRouteData({ - props: { ...route, headings: [{ depth: 1, slug: 'heading-1', text: 'Heading 1' }] }, - url: new URL('https://example.com'), - }); - expect(data.labels).toBeDefined(); - expect(data.labels['skipLink.label']).toBe('Aller au contenu'); -}); - -test('includes localized labels (pt-br)', () => { - const route = routes[1]!; - const data = generateRouteData({ - props: { ...route, headings: [{ depth: 1, slug: 'heading-1', text: 'Heading 1' }] }, - url: new URL('https://example.com'), - }); - expect(data.labels).toBeDefined(); - expect(data.labels['skipLink.label']).toBe('Pular para o conteúdo'); -}); diff --git a/packages/starlight/__tests__/i18n/translations-fs.test.ts b/packages/starlight/__tests__/i18n/translations-fs.test.ts index 5b9025aa950..0115f1d355d 100644 --- a/packages/starlight/__tests__/i18n/translations-fs.test.ts +++ b/packages/starlight/__tests__/i18n/translations-fs.test.ts @@ -73,4 +73,32 @@ describe('createTranslationSystemFromFs', () => { ) ).toThrow(SyntaxError); }); + + test('creates a translation system that uses custom strings injected by plugins', () => { + const useTranslations = createTranslationSystemFromFs( + { + locales: { en: { label: 'English', dir: 'ltr' } }, + defaultLocale: { label: 'English', locale: 'en', dir: 'ltr' }, + }, + // Using non-existent `_src/` to ignore custom files in this test fixture. + { srcDir: new URL('./_src/', import.meta.url) }, + { en: { 'page.editLink': 'Make this page even more different' } } + ); + const t = useTranslations('en'); + expect(t('page.editLink')).toMatchInlineSnapshot('"Make this page even more different"'); + }); + + test('creates a translation system that prioritizes user translations over plugin translations', () => { + const useTranslations = createTranslationSystemFromFs( + { + locales: { en: { label: 'English', dir: 'ltr' } }, + defaultLocale: { label: 'English', locale: 'en', dir: 'ltr' }, + }, + // Using `src/` to load custom files in this test fixture. + { srcDir: new URL('./src/', import.meta.url) }, + { en: { 'page.editLink': 'Make this page even more different' } } + ); + const t = useTranslations('en'); + expect(t('page.editLink')).toMatchInlineSnapshot('"Make this page different"'); + }); }); diff --git a/packages/starlight/__tests__/i18n/translations.test.ts b/packages/starlight/__tests__/i18n/translations.test.ts index 35cd94916a9..e0c0ccbef37 100644 --- a/packages/starlight/__tests__/i18n/translations.test.ts +++ b/packages/starlight/__tests__/i18n/translations.test.ts @@ -28,3 +28,11 @@ describe('useTranslations()', () => { expect(t('page.nextLink')).not.toBe(translations.en?.['page.nextLink']); }); }); + +describe('t.dir()', async () => { + test('returns text directions', () => { + expect(useTranslations(undefined).dir()).toBe('ltr'); + expect(useTranslations('fr').dir()).toBe('ltr'); + expect(useTranslations('ar').dir()).toBe('rtl'); + }); +}); diff --git a/packages/starlight/__tests__/plugins/translations.test.ts b/packages/starlight/__tests__/plugins/translations.test.ts new file mode 100644 index 00000000000..3f6574f7297 --- /dev/null +++ b/packages/starlight/__tests__/plugins/translations.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, test, vi } from 'vitest'; +import { useTranslations } from '../../utils/translations'; + +vi.mock('astro:content', async () => + (await import('../test-utils')).mockedAstroContent({ + // We do not strip unknown translations in this test so that user-defined translations can + // override plugin translations like it would in a real- world scenario were the plugin would + // have provided a custom schema to extend the translations. + i18n: [['ar', { 'testPlugin3.doThing': 'افعل الشيء' }, { stripUnknown: false }]], + }) +); + +describe('useTranslations()', () => { + test('includes UI strings injected by plugins for the default locale', () => { + const t = useTranslations(undefined); + expect(t).toBeTypeOf('function'); + // Include the default locale strings. + expect(t('skipLink.label')).toBe('Skip to content'); + // Include a built-in translation overriden by a plugin. + expect(t('search.label')).toBe('Search the thing'); + // Include a translation injected by a plugin. + // @ts-expect-error - translation key injected by a test plugin. + expect(t('testPlugin3.doThing')).toBe('Do the Plugin 3 thing'); + }); + + test('includes UI strings injected by plugins', () => { + const t = useTranslations('fr'); + // Include the default locale strings. + expect(t('skipLink.label')).toBe('Aller au contenu'); + // Include a built-in translation overriden by a plugin. + expect(t('search.label')).toBe('Rechercher le truc'); + // Include a translation injected by a plugin. + // @ts-expect-error - translation key injected by a test plugin. + expect(t('testPlugin3.doThing')).toBe('Faire la chose du plugin 3'); + }); + + test('uses user-defined translations for untranslated strings injected by plugins', () => { + const t = useTranslations('pt-br'); + // @ts-expect-error - translation key injected by a test plugin. + expect(t('testPlugin3.doThing')).toBe('Do the Plugin 3 thing'); + }); + + test('prefers user-defined translations over plugin translations', () => { + const t = useTranslations('ar'); + // @ts-expect-error - translation key injected by a test plugin. + expect(t('testPlugin3.doThing')).toBe('افعل الشيء'); + }); +}); diff --git a/packages/starlight/__tests__/plugins/vitest.config.ts b/packages/starlight/__tests__/plugins/vitest.config.ts index a6eaa3b296e..78c4c254204 100644 --- a/packages/starlight/__tests__/plugins/vitest.config.ts +++ b/packages/starlight/__tests__/plugins/vitest.config.ts @@ -3,6 +3,13 @@ import { defineVitestConfig } from '../test-config'; export default defineVitestConfig({ title: 'Plugins', sidebar: [{ label: 'Getting Started', link: 'getting-started' }], + defaultLocale: 'en', + locales: { + en: { label: 'English', lang: 'en' }, + fr: { label: 'French' }, + ar: { label: 'Arabic', dir: 'rtl' }, + 'pt-br': { label: 'Brazilian Portuguese', lang: 'pt-BR' }, + }, plugins: [ { name: 'test-plugin-1', @@ -37,11 +44,24 @@ export default defineVitestConfig({ { name: 'test-plugin-3', hooks: { - async setup({ config, updateConfig }) { + async setup({ config, updateConfig, injectTranslations }) { await Promise.resolve(); updateConfig({ description: `${config.description} - plugin 3`, }); + injectTranslations({ + en: { + 'search.label': 'Search the thing', + 'testPlugin3.doThing': 'Do the Plugin 3 thing', + }, + fr: { + 'search.label': 'Rechercher le truc', + 'testPlugin3.doThing': 'Faire la chose du plugin 3', + }, + ar: { + 'testPlugin3.doThing': 'قم بعمل المكون الإضافي 3', + }, + }); }, }, }, diff --git a/packages/starlight/__tests__/test-config.ts b/packages/starlight/__tests__/test-config.ts index b1a34440b9e..47ab267d211 100644 --- a/packages/starlight/__tests__/test-config.ts +++ b/packages/starlight/__tests__/test-config.ts @@ -20,15 +20,24 @@ export async function defineVitestConfig( const trailingSlash = opts?.trailingSlash ?? 'ignore'; const command = opts?.command ?? 'dev'; - const { starlightConfig } = await runPlugins(config, plugins, createTestPluginContext()); + const { starlightConfig, pluginTranslations } = await runPlugins( + config, + plugins, + createTestPluginContext() + ); return getViteConfig({ plugins: [ - vitePluginStarlightUserConfig(command, starlightConfig, { - root, - srcDir, - build, - trailingSlash, - }), + vitePluginStarlightUserConfig( + command, + starlightConfig, + { + root, + srcDir, + build, + trailingSlash, + }, + pluginTranslations + ), ], test: { snapshotSerializers: ['./snapshot-serializer-astro-error.ts'], diff --git a/packages/starlight/__tests__/test-utils.ts b/packages/starlight/__tests__/test-utils.ts index 2e821544d40..69b01edaf2e 100644 --- a/packages/starlight/__tests__/test-utils.ts +++ b/packages/starlight/__tests__/test-utils.ts @@ -37,8 +37,17 @@ function mockDoc( }; } -function mockDict(id: string, data: z.input>) { - return { id, data: i18nSchema().parse(data) }; +function mockDict( + id: string, + data: z.input>, + { stripUnknown } = { stripUnknown: true } +) { + return { + id, + data: stripUnknown + ? i18nSchema().parse(data) + : i18nSchema().and(z.record(z.string())).parse(data), + }; } export async function mockedAstroContent({ diff --git a/packages/starlight/components/DraftContentNotice.astro b/packages/starlight/components/DraftContentNotice.astro index 67cd0466de8..70d0d4fab73 100644 --- a/packages/starlight/components/DraftContentNotice.astro +++ b/packages/starlight/components/DraftContentNotice.astro @@ -1,8 +1,6 @@ --- import ContentNotice from './ContentNotice.astro'; import type { Props } from '../props'; - -const { labels } = Astro.props; --- - + diff --git a/packages/starlight/components/EditLink.astro b/packages/starlight/components/EditLink.astro index 74be4c8f79d..711d3999ba6 100644 --- a/packages/starlight/components/EditLink.astro +++ b/packages/starlight/components/EditLink.astro @@ -2,14 +2,14 @@ import Icon from '../user-components/Icon.astro'; import type { Props } from '../props'; -const { editUrl, labels } = Astro.props; +const { editUrl } = Astro.props; --- { editUrl && ( - {labels['page.editLink']} + {Astro.locals.t('page.editLink')} ) } diff --git a/packages/starlight/components/FallbackContentNotice.astro b/packages/starlight/components/FallbackContentNotice.astro index b3474fb28fb..616d06fe929 100644 --- a/packages/starlight/components/FallbackContentNotice.astro +++ b/packages/starlight/components/FallbackContentNotice.astro @@ -1,8 +1,6 @@ --- import ContentNotice from './ContentNotice.astro'; import type { Props } from '../props'; - -const { labels } = Astro.props; --- - + diff --git a/packages/starlight/components/Footer.astro b/packages/starlight/components/Footer.astro index f75b5e40703..0bba68258c3 100644 --- a/packages/starlight/components/Footer.astro +++ b/packages/starlight/components/Footer.astro @@ -18,7 +18,7 @@ import { Icon } from '../components'; { config.credits && ( - {Astro.props.labels['builtWithStarlight.label']} + {Astro.locals.t('builtWithStarlight.label')} ) } diff --git a/packages/starlight/components/LanguageSelect.astro b/packages/starlight/components/LanguageSelect.astro index c85292dc7bb..42999eac9b2 100644 --- a/packages/starlight/components/LanguageSelect.astro +++ b/packages/starlight/components/LanguageSelect.astro @@ -10,8 +10,6 @@ import type { Props } from '../props'; function localizedPathname(locale: string | undefined): string { return localizedUrl(Astro.url, locale).pathname; } - -const { labels } = Astro.props; --- { @@ -19,7 +17,7 @@ const { labels } = Astro.props; diff --git a/packages/starlight/i18n.d.ts b/packages/starlight/i18n.d.ts new file mode 100644 index 00000000000..8cb82217cd7 --- /dev/null +++ b/packages/starlight/i18n.d.ts @@ -0,0 +1,18 @@ +/* + * This file imports the original `i18next` types and extends them to configure the + * Starlight namespace. + * + * Note that the top-level `import` makes this module non-ambient, so can’t be + * combined with other `.d.ts` files such as `locals.d.ts`. + */ + +import 'i18next'; + +declare module 'i18next' { + interface CustomTypeOptions { + defaultNS: typeof import('./utils/createTranslationSystem').I18nextNamespace; + resources: { + starlight: Record; + }; + } +} diff --git a/packages/starlight/index.ts b/packages/starlight/index.ts index d2a5e572eb7..50b1a87ea0c 100644 --- a/packages/starlight/index.ts +++ b/packages/starlight/index.ts @@ -1,3 +1,11 @@ +/** + * These triple-slash directives defines dependencies to various declaration files that will be + * loaded when a user imports the Starlight integration in their Astro configuration file. These + * directives must be first at the top of the file and can only be preceded by this comment. + */ +/// +/// + import mdx from '@astrojs/mdx'; import type { AstroIntegration } from 'astro'; import { spawn } from 'node:child_process'; @@ -9,7 +17,12 @@ import { starlightSitemap } from './integrations/sitemap'; import { vitePluginStarlightUserConfig } from './integrations/virtual-user-config'; import { rehypeRtlCodeSupport } from './integrations/code-rtl-support'; import { createTranslationSystemFromFs } from './utils/translations-fs'; -import { runPlugins, type StarlightUserConfigWithPlugins } from './utils/plugins'; +import { + injectPluginTranslationsTypes, + runPlugins, + type PluginTranslations, + type StarlightUserConfigWithPlugins, +} from './utils/plugins'; import { processI18nConfig } from './utils/i18n'; import type { StarlightConfig } from './types'; @@ -18,10 +31,12 @@ export default function StarlightIntegration({ ...opts }: StarlightUserConfigWithPlugins): AstroIntegration { let userConfig: StarlightConfig; + let pluginTranslations: PluginTranslations = {}; return { name: '@astrojs/starlight', hooks: { 'astro:config:setup': async ({ + addMiddleware, command, config, injectRoute, @@ -42,10 +57,17 @@ export default function StarlightIntegration({ config.i18n ); - const { integrations } = pluginResult; + const integrations = pluginResult.integrations; + pluginTranslations = pluginResult.pluginTranslations; userConfig = starlightConfig; - const useTranslations = createTranslationSystemFromFs(starlightConfig, config); + const useTranslations = createTranslationSystemFromFs( + starlightConfig, + config, + pluginTranslations + ); + + addMiddleware({ entrypoint: '@astrojs/starlight/locals', order: 'pre' }); if (!starlightConfig.disable404Route) { injectRoute({ @@ -91,7 +113,9 @@ export default function StarlightIntegration({ updateConfig({ vite: { - plugins: [vitePluginStarlightUserConfig(command, starlightConfig, config)], + plugins: [ + vitePluginStarlightUserConfig(command, starlightConfig, config, pluginTranslations), + ], }, markdown: { remarkPlugins: [ @@ -112,6 +136,10 @@ export default function StarlightIntegration({ }); }, + 'astro:config:done': ({ injectTypes }) => { + injectPluginTranslationsTypes(pluginTranslations, injectTypes); + }, + 'astro:build:done': ({ dir }) => { if (!userConfig.pagefind) return; const targetDir = fileURLToPath(dir); diff --git a/packages/starlight/integrations/asides.ts b/packages/starlight/integrations/asides.ts index 7c4dfb74f24..00ca11a603c 100644 --- a/packages/starlight/integrations/asides.ts +++ b/packages/starlight/integrations/asides.ts @@ -166,7 +166,7 @@ function remarkAsides(options: AsidesOptions): Plugin<[], Root> { // children with the `directiveLabel` property set to true. We want to pass it as the title // prop to