Tabla de contenido
En esta publicaci贸n, vamos a adentrarnos en un enfoque para la gesti贸n de literales de un proyecto de React utilizando react-intl.
Configurando un nuevo proyecto con create-react-app
Iniciamos un nuevo proyecto de React con CRA, una de las formas m谩s f谩ciles de hacerlo sin tener que lidiar con la configuraci贸n de webpack.
npx create-react-app react-intl-example --template typescript
cd react-intl-example
npm start
Instalando react-intl
El siguiente paso es instalar react-intl
como una dependencia:
npm i react-intl
Tambi茅n necesitaremos una dependencia de desarrollo para extraer y compilar los literales:
npm i -D @formatjs/cli
Usando react-intl
en una aplicaci贸n de React
Comencemos a usar react-intl
en nuestra aplicaci贸n de React de prueba.
Crea una carpeta lang
en src
con un archivo JSON vac铆o llamado en.json
. Este archivo contendr谩 m谩s tarde el resultado de la compilaci贸n para cada literal en la aplicaci贸n. Explicaremos m谩s adelante en detalle c贸mo lograr esto.
{}
Editamos index.tsx
en la carpeta src
. Tenemos que importar IntlProvider
de react-intl
y envolver nuestra aplicaci贸n con 茅l. Estamos creando algunos componentes para rich elements que por defecto se usar谩n globalmente en los literales de la aplicaci贸n.
|
|
El siguiente paso es crear una nueva carpeta con un archivo example.ts
donde definiremos algunos literales. Por ejemplo, uno usando un rich element global definido anteriormente (usado como una etiqueta HTML, <bold></bold>
en este ejemplo), y otro literal regular simple. Lo haremos utilizando la funci贸n defineMessages
de react-intl
.
import { defineMessages } from "react-intl";
export default defineMessages({
hello: {
id: "a.hello",
defaultMessage: "<bold>hello</bold>",
},
world: {
id: "a.world",
defaultMessage: "world",
},
});
Podemos crear otro archivo de literales llamado other.ts
dentro de la carpeta messages
. Esto es solo un ejemplo, puedes crear tantos archivos de literales como desees y colocarlos donde quieras, esa decisi贸n est谩 en tus manos y es subjetiva.
import { defineMessages } from "react-intl";
export default defineMessages({
other: {
id: "a.richtext",
defaultMessage: "I have <test>{num}</test>",
},
});
Ahora podemos comenzar a importar nuestros literales en la aplicaci贸n. Para simplificar, mostrar茅 c贸mo hacerlo editando directamente el archivo App.tsx
de la siguiente manera:
import React from "react";
import { FormattedMessage } from "react-intl";
import exampleMessages from "./messages/example";
import otherMessages from "./messages/other";
function App() {
return (
<div className="App">
<FormattedMessage {...exampleMessages.hello} />{" "}
<FormattedMessage {...exampleMessages.world} />
<FormattedMessage
id={otherMessages.other.id}
defaultMessage={otherMessages.other.defaultMessage}
values={{ num: 99, test: (chunks: any) => <strong>{chunks}!!</strong> }}
/>
</div>
);
}
export default App;
En este ejemplo, estamos cargando nuestros literales desde dos archivos diferentes y utilizando el componente FormattedMessage
de la biblioteca react-intl
. Puedes consultar en la documentaci贸n oficial diferentes formas de declarar mensajes.
Por lo tanto, cada vez que deseemos usar un literal en la aplicaci贸n, podemos definirlo en un archivo separado e importarlo para usarlo. Si no se encuentra un literal en el archivo compilado lang/en.json
, se utilizar谩 el valor proporcionado en defaultMessage
. Esto es muy 煤til porque no es necesario compilar los literales cada vez que necesitemos un nuevo literal mientras desarrollamos.
Extrayendo y compilando literales con formatJS
Crearemos algunos scripts en el archivo package.json
para automatizar el proceso de extracci贸n y compilaci贸n de literales. Podemos consultar la documentaci贸n oficial para obtener m谩s detalles. Utilizaremos un comando muy largo de la documentaci贸n y lo dividiremos en algunos scripts para una mejor legibilidad. Veamos los scripts y luego la explicaci贸n de los comandos ejecutados:
// ---- scripts section from package.json
"scripts": {
"literals:extract": "formatjs extract 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file temp.json --flatten --id-interpolation-pattern '[sha512:contenthash:base64:6]'",
"literals:compile": "formatjs compile 'temp.json'",
"postliterals:compile": "rm temp.json",
}
// ----
El primer comando extraer谩 cada una de los literales definidos en la aplicaci贸n bajo la carpeta src
a un archivo temporal llamado temp.json
.
formatjs extract 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file temp.json --flatten --id-interpolation-pattern '[sha512:contenthash:base64:6]'
Advertencias: El comando incluye una opci贸n para generar ids, pero con la configuraci贸n de
create-react-app
, esta caracter铆stica no funcionar谩 porque es necesario editar la configuraci贸n de webpack y babel.
Una vez extra铆dos los literales a un archivo, los compilaremos especificando el archivo de destino (destacar los par谩metros adicionales utilizados en el comando, no est谩n presentes en el script anterior, agregaremos m谩s scripts al final):
formatjs compile 'temp.json' --out-file src/lang/en.json
Con este comando, el archivo generado (src/lang/en.json
) ser谩 el siguiente:
{
"a.hello": "<bold>hello</bold>",
"a.richtext": "I have <test>{num}</test>",
"a.world": "world"
}
Si deseamos traducir nuestros literales a otro idioma, este archivo debe ser utilizado como punto de partida, y se deben traducir cada uno de los literales. Luego, debemos crear un nuevo archivo, por ejemplo
es.json
, y agregar l贸gica enindex.tsx
para cargar el archivoen.json
oes.json
dependiendo del idioma seleccionado en la aplicaci贸n.
Si iniciamos nuestra aplicaci贸n con este archivo, todo funcionar谩 seg煤n lo esperado, pero en las DevTools se puede leer esta advertencia:
[@formatjs/intl] "defaultRichTextElements" was specified but "message" was not pre-compiled. Please consider using "@formatjs/cli" to pre-compile your messages for performance. For more details see https://formatjs.io/docs/getting-started/message-distribution
.
Esto se debe a que estamos usando la opci贸n defaultRichTextElements
globalmente para los literales, y cada vez que se carga un literal, la biblioteca no sabe si el literal est谩 utilizando defaultRichTextElements
o no. Por esta raz贸n, debemos compilar utilizando el flag ast
:
formatjs compile 'temp.json' --ast --out-file src/lang/en.json
Despu茅s de esto, el resultado ser谩 diferente:
{
"a.hello": [
{
"children": [
{
"type": 0,
"value": "hello"
}
],
"type": 8,
"value": "bold"
}
],
"a.richtext": [
{
"type": 0,
"value": "I have "
},
{
"children": [
{
"type": 1,
"value": "num"
}
],
"type": 8,
"value": "test"
}
],
"a.world": [
{
"type": 0,
"value": "world"
}
]
}
Y si iniciamos la aplicaci贸n con este archivo, la advertencia desaparecer谩. Finalmente, podemos agregar m谩s scripts con los flags necesarios para cada caso. Extracto final de los scripts:
// ---- scripts section from package.json
"scripts": {
"literals:extract": "formatjs extract 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file temp.json --flatten --id-interpolation-pattern '[sha512:contenthash:base64:6]'",
"literals:compile": "formatjs compile 'temp.json'",
"postliterals:compile": "rm temp.json",
"literals:en": "npm run literals:compile -- --out-file src/lang/en.json",
"literals:en:ast": "npm run literals:compile -- --ast --out-file src/lang/en.json",
"literals:extract:compile": "npm-run-all literals:extract literals:en",
"literals:extract:compile:ast": "npm-run-all literals:extract literals:en:ast"
}
// ----
Los 煤ltimos 2 scripts est谩n utilizando
npm-run-all
como una dependencia de desarrollo, puedes instalarla usando:npm i -D npm-run-all
.
Para generar los literales en la aplicaci贸n, podemos ejecutar literals:extract:compile
para generar un archivo listo para ser traducido, o literals:extract:compile:ast
para un archivo listo para producci贸n.
Se puede revisar un repositorio con este ejemplo de aplicaci贸n en mi cuenta de Github.
Nota: No he probado BalbelEdit pero parece que puede ser muy 煤til a la hora de traducir una aplicaci贸n, soporta React.