Uso de Markdown y localización en el editor de bloques de WordPress

Índice
  1. Cargando contenido de Markdown
  2. Traducir la documentación al idioma del usuario.
  3. Introducir el idioma del usuario en el bloque.
  4. Importaciones dinámicas
  5. Manejando los trozos
  6. Establecer el camino público para los fragmentos
  7. Volver a un idioma predeterminado
  8. Configurar la documentación en el modal.
  9. ¿Tadaaaaaaaa?

Si necesitamos mostrar documentación al usuario directamente en el editor de WordPress, ¿cuál es la mejor forma de hacerlo?

Dado que el editor de bloques se basa en React, podemos sentirnos tentados a utilizar componentes de React y código HTML para la documentación. Ese es el enfoque que seguí en mi artículo anterior, que demostró una manera de mostrar documentación en una ventana modal.

Pero esta solución no es perfecta, porque agregar documentación a través de componentes React y código HTML podría volverse muy detallado, por no mencionar difícil de mantener. Por ejemplo, el modal de la imagen de arriba contiene la documentación en un componente de React como este:

const CacheControlDescription = () = {  return (    pThe Cache-Control header will contain the minimum max-age value from all fields/directives involved in the request, or codeno-store/code if the max-age is 0/p  )}

Usar Markdown en lugar de HTML puede facilitar el trabajo. Por ejemplo, la documentación anterior podría sacarse del componente React y colocarse en un archivo Markdown como /docs/cache-control.md:

The Cache-Control header will contain the minimum max-age value from all fields/directives involved in the request, or `no-store` if the max-age is 0

¿Cuáles son los beneficios y desventajas de usar Markdown en comparación con HTML puro?

Ventaja Desventajas
✅ Escribir Markdown es más fácil y rápido que HTML ❌ La documentación no puede contener componentes de React.
✅ La documentación se puede mantener separada del código fuente del bloque (incluso en un repositorio separado) ❌ No podemos utilizar la __función (que ayuda a localizar el contenido a través .pode archivos) para generar texto
✅ Los editores pueden modificar la documentación sin temor a romper el código.
✅ El código de documentación no se agrega al activo JavaScript del bloque, que luego puede cargar más rápido

En cuanto a los inconvenientes, no poder utilizar los componentes de React puede no ser un problema, al menos para la documentación sencilla. Sin embargo, la falta de localización es un problema importante. El texto del componente React agregado a través de la __función JavaScript se puede extraer y reemplazar mediante traducciones de archivos POT. El contenido de Markdown no puede acceder a esta funcionalidad.

La localización de soporte para la documentación es obligatoria, por lo que tendremos que compensarla. En este artículo perseguiremos dos objetivos:

  • Usar Markdown para escribir documentación (que se muestra en un bloque del editor de WordPress)
  • Traducir la documentación al idioma del usuario.

¡Empecemos!

Cargando contenido de Markdown

Habiendo creado un archivo Markdown /docs/cache-control.md, podemos importar su contenido (ya renderizado como HTML) e inyectarlo en el componente React de esta manera:

import CacheControlDocumentation from '../docs/cache-control.md';
const CacheControlDescription = () = {  return (    div      dangerouslySetInnerHTML={ { __html: CacheControlDocumentation } }    /  );}

Esta solución se basa en webpack, el paquete de módulos que se encuentra en el núcleo del editor de WordPress.

Tenga en cuenta que el editor de WordPress utiliza actualmente el paquete web 4.42. Sin embargo, la documentación que se muestra al principio en el sitio del paquete web corresponde a la versión 5 (que todavía está en versión beta). La documentación para la versión 4 se encuentra en una sustitución.

El contenido se transforma de Markdown a HTML a través de los cargadores de webpack, para lo cual el bloque necesita personalizar su configuración de webpack, agregando las reglas para usar markdown-loader y html-loader.

Para hacer esto, agregue un archivo, webpack.config.jsen la raíz del bloque con este código:

// This is the default webpack configuration from Gutenbergconst defaultConfig = require( '@wordpress/scripts/config/webpack.config' );
// Customize adding the required rules for the blockmodule.exports = {  ...defaultConfig,  module: {    ...defaultConfig.module,    rules: [      ...defaultConfig.module.rules,      {        test: /.md$/,        use: [          {            loader: "html-loader"          },          {            loader: "markdown-loader"          }        ]      }    ],  },};

E instale los paquetes correspondientes:

npm install --save-dev markdown-loader html-loader

Apliquemos una pequeña mejora mientras estamos en ello. La carpeta de documentos podría contener la documentación de los componentes ubicados en cualquier parte del proyecto. Para evitar tener que calcular la ruta relativa de cada componente a esa carpeta, podemos agregar un alias, @docspara webpack.config.jsresolver la carpeta /docs:

const path = require( 'path' );config.resolve.alias[ '@docs' ] = path.resolve( process.cwd(), 'docs/' )

Ahora, las importaciones están simplificadas:

import CacheControlDocumentation from '@docs/cache-control.md';

¡Eso es todo! Ahora podemos inyectar documentación de archivos Markdown externos en el componente React.

Traducir la documentación al idioma del usuario.

No podemos traducir cadenas a través de .poarchivos para contenido de Markdown, pero existe una alternativa: producir diferentes archivos de Markdown para diferentes idiomas. Entonces, en lugar de tener un solo archivo ( /docs/cache-control.md), podemos tener un archivo por idioma, cada uno almacenado bajo el código de idioma correspondiente:

  • /docs/en/cache-control.md
  • /docs/fr/cache-control.md
  • /docs/zh/cache-control.md
  • etc.

También podríamos admitir traducciones tanto para el idioma como para la región, de modo que el inglés americano y británico puedan tener versiones diferentes, y usar de forma predeterminada la versión de solo idioma cuando no se proporciona una traducción para una región (por ejemplo, "en_CA"la maneja "en"):

  • /docs/en_US/cache-control.md
  • /docs/en_GB/cache-control.md
  • /docs/en/cache-control.md

Para simplificar las cosas, sólo explicaré cómo admitir diferentes idiomas, sin regiones. Pero el código es prácticamente el mismo.

El código demostrado en este artículo también se puede ver en el código fuente de un complemento de WordPress que creó.

Introducir el idioma del usuario en el bloque.

El idioma del usuario en WordPress se puede recuperar desde get_locale(). Dado que la configuración regional incluye el código de idioma y la región (como "en_US"), la analizamos para extraer el código de idioma por sí mismo:

function get_locale_language(): string {  $localeParts = explode( '_', get_locale() );  return $localeParts[0];}

A través de wp_localize_script(), proporcionamos el código de idioma al bloque, como userLangpropiedad bajo una variable global (que, en este caso, es graphqlApiCacheControl):

// The block was registered as $blockScriptRegistrationNamewp_localize_script(  $blockScriptRegistrationName,  'graphqlApiCacheControl',  [    'userLang' = get_locale_language(),  ]);

Ahora el código de idioma del usuario está disponible en el bloque:

const lang = window.graphqlApiCacheControl.userLang; 

Importaciones dinámicas

Sólo podemos conocer el idioma del usuario en tiempo de ejecución. Sin embargo, la importafirmación es estática, no dinámica. Por lo tanto, no podemos hacer esto:

// `lang` contains the user's languageimport CacheControlDocumentation from '@docs/${ lang }/cache-control.md';

Dicho esto, webpack nos permite cargar módulos dinámicamente a través de la importfunción que, de forma predeterminada, divide el módulo solicitado en un fragmento separado (es decir, no está incluido en el build/index.jsarchivo compilado principal) para cargarlo de forma diferida.

Este comportamiento es adecuado para mostrar documentación en una ventana modal, que se activa mediante una acción del usuario y no se carga por adelantado. importDebe recibir alguna información sobre dónde está ubicado el módulo, por lo que este código funciona:

import( `@docs/${ lang }/cache-control.md` ).then( module = {  // ...});

Pero este código aparentemente similar no:

const dynamicModule = `@docs/${ lang }/cache-control.md`import( dynamicModule ).then( module = {  // ...});

Se puede acceder al contenido del archivo bajo la clave defaultdel objeto importado:

const cacheControlContent = import( `@docs/${ lang }/cache-control.md` ).then( obj = obj.default )

Podemos generalizar esta lógica en una función llamada getMarkdownContent, pasando el nombre del archivo Markdown junto con el idioma:

const getMarkdownContent = ( fileName, lang ) = {  return import( `@docs/${ lang }/${ fileName }.md` )    .then( obj = obj.default )} 

Manejando los trozos

Para mantener organizados los recursos del bloque, mantengamos los fragmentos de documentación agrupados en la /docssubcarpeta (que se crearán dentro de la build/carpeta) y les daremos nombres de archivo descriptivos.

Luego, al tener dos documentos ( cache-control.mdy cache-purging.md) en tres idiomas (inglés, francés y chino mandarín), se producirán los siguientes fragmentos:

  • build/docs/en-cache-control-md.js
  • build/docs/fr-cache-control-md.js
  • build/docs/zh-cache-control-md.js
  • build/docs/en-cache-purging-md.js
  • build/docs/fr-cache-purging-md.js
  • build/docs/zh-cache-purging-md.js

Esto se logra usando el comentario mágico /* webpackChunkName: "docs/[request]" */justo antes del importargumento:

const getMarkdownContent = ( fileName, lang ) = {  return import( /* webpackChunkName: "docs/[request]" */ `@docs/${ lang }/${ fileName }.md` )    .then(obj = obj.default)} 

Establecer el camino público para los fragmentos

webpack sabe dónde buscar los fragmentos, gracias a la publicPathopción de configuración. Si no se proporciona, se utiliza la URL actual del editor de WordPress, /wp-admin/lo que genera un 404 ya que los fragmentos están ubicados en otro lugar. Para mi bloque, están por debajo de /wp-content/plugins/graphql-api/blocks/cache-control/build/.

Si el bloque es para nuestro propio uso, podemos codificarlo publicPatho webpack.config.js,proporcionarlo a través de una ASSET_PATHvariable de entorno. De lo contrario, debemos pasar la ruta pública al bloque en tiempo de ejecución. Para ello calculamos la URL de la build/carpeta del bloque:

$blockPublicPath = plugin_dir_url( __FILE__ ) . '/blocks/cache-control/build/';

Luego lo inyectamos en el lado de JavaScript localizando el bloque:

// The block was registered as $blockScriptRegistrationNamewp_localize_script(    $blockScriptRegistrationName,    'graphqlApiCacheControl',    [      //...      'publicPath' = $blockPublicPath,    ]);

Y luego proporcionamos la ruta pública a la __webpack_public_path__variable JavaScript:

__webpack_public_path__ = window.graphqlApiCacheControl.publicPath;

Volver a un idioma predeterminado

¿Qué pasaría si no hay traducción para el idioma del usuario? En ese caso, llamar getMarkdownContentarrojará un error.

Por ejemplo, cuando el idioma está configurado en alemán, la consola del navegador mostrará esto:

Uncaught (in promise) Error: Cannot find module './de/cache-control.md'

La solución es detectar el error y luego devolver el contenido en un idioma predeterminado, que el bloque siempre satisface:

const getMarkdownContentOrUseDefault = ( fileName, defaultLang, lang ) = {  return getMarkdownContent( fileName, lang )    .catch( err = getMarkdownContent( fileName, defaultLang ) )}

Tenga en cuenta el comportamiento diferente al codificar la documentación como HTML dentro del componente React y como un archivo Markdown externo, cuando la traducción está incompleta. En el primer caso, si una cadena ha sido traducida pero otra no (en el .poarchivo), entonces el componente React terminará mostrando idiomas mixtos. En el segundo caso es todo o nada: o la documentación está completamente traducida o no.

Configurar la documentación en el modal.

Ahora podemos recuperar la documentación del archivo Markdown. Veamos cómo mostrarlo en el modal.

Primero empaquetamos Modalel componente de Gutenberg para inyectar el contenido como HTML:

import { Modal } from '@wordpress/components';
const ContentModal = ( props ) = {  const { content } = props;  return (    Modal       { ...props }          div        dangerouslySetInnerHTML={ { __html: content } }      /    /Modal  );};

Luego recuperamos el contenido del archivo Markdown y lo pasamos al modal como accesorio usando un enlace de estado llamado page. Cargar contenido dinámicamente es una operación asíncrona, por lo que también debemos usar un enlace de efecto para realizar un efecto secundario en el componente. Necesitamos leer el contenido del archivo Markdown solo una vez, por lo que pasamos una matriz vacía como segundo argumento useEffect(o el gancho se seguirá activando):

import { useState, useEffect } from '@wordpress/element';
const CacheControlContentModal = ( props ) = {  const fileName = 'cache-control'  const lang = window.graphqlApiCacheControl.userLang  const defaultLang = 'en'
  const [ page, setPage ] = useState( [] );
  useEffect(() = {    getMarkdownContentOrUseDefault( fileName, defaultLang, lang ).then( value = {      setPage( value )    });  }, [] );
  return (    ContentModal      { ...props }      content={ page }    /  );};

Veámoslo funcionar. Observe cómo el fragmento que contiene la documentación se carga de forma diferida (es decir, se activa cuando se edita el bloque):

¿Tadaaaaaaaa?

Puede que escribir documentación no sea lo que más le guste en el mundo, pero facilitar su redacción y mantenimiento puede ayudar a aliviarlo.

Usar Markdown en lugar de HTML puro es sin duda una forma de hacerlo. Espero que el enfoque que acabamos de cubrir no sólo mejore su flujo de trabajo, sino que también le brinde una mejora agradable para sus usuarios de WordPress.

SUSCRÍBETE A NUESTRO BOLETÍN 
No te pierdas de nuestro contenido ni de ninguna de nuestras guías para que puedas avanzar en los juegos que más te gustan.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir

Este sitio web utiliza cookies para mejorar tu experiencia mientras navegas por él. Este sitio web utiliza cookies para mejorar tu experiencia de usuario. Al continuar navegando, aceptas su uso. Mas informacion