maizzle / framework

Quickly build HTML emails with Tailwind CSS.
https://maizzle.com
MIT License
1.25k stars 48 forks source link

Internationalization - i18n support #1363

Closed manquer closed 1 month ago

manquer commented 1 month ago

Currently there doesn't seem to be easy way to add native support for multi-lingual templates.

After considering many options I think the best way would be to set up the templates under folder hierarchy

├── components
│   ├── button.html
│   ├── divider.html
│   ├── spacer.html
│   └── v-fill.html
├── config.mjs
├── config.production.mjs
├── en-US
│   └── transactional.html
├── fr-FR
│   └── transactional.html
...
├── images
│   ├── logo.png
└── layouts
    └── main.html

and pass the correct input string in the API with locale with something like this

import { render } from '@maizzle/framework'
import tailwindcssPresetEmail from 'tailwindcss-preset-email'
import fs from 'fs'
...

const input  = fs.readFileSync(`${locale}/transactional.html`).toString('utf-8')
// could be more advanced to resolve from file in default locale is not found in preferred one 

const { html } = await render(input, {
  css: {
    tailwind: {
      presets: [tailwindcssPresetEmail],
      content: [
        {
          raw: input,
          extension: 'html'
        }
      ]
    }
  }
})

However this only works if components do not have any hard coded text inside them. Maizzle does not support selecting components from a locale and fallback to default without customizing the framework.

The other options I have considered is to useTransformers or beforeRender event hook. However they both work on HTML strings rather than text, that will turn out to either to walk the DOM with XML/HTML parsing libraries or end up with some form of regex/string parsing😱[1]

I have looked into beforeCreate hook that takes config with options to be sent to Markdown parser for html there isnt any simple workflow, there are few other options like replaceStrings that i could setup dynamic or multiple environments to use with, but all seem to lead to string parsing.

Finally I am considering localAttr in each component for text and evaluate the expression of text using a library like i18next[2].

It would be nice to have an i18n helper inbuilt or core framework support or can you perhaps suggest a simpler options I have missed?

[1] https://stackoverflow.com/a/1732454/1900483 [2] https://github.com/i18next/i18next

cossssmin commented 1 month ago

For the components, you'll need to specify the folders they exist in:

https://next.maizzle.com/docs/configuration/components#folders

I'm not sure I want an opinionated way of doing i18n built into core, I think a starter project with i18next would be more suitable.