srid/emanote

i18n

srid opened this issue · 5 comments

srid commented

What can Emanote do to better support multilingual websites?

For original requirement, see this issue nixos-asia/website#18 (cc @brsvh)

Tasks

  • #485
  • .tpl templates should support translated strings (based on page.lang)
  • ...
brsvh commented

I am currently attempting to move the templates into directories named after their respective languages, such as {en,zh-Hans}/components/sidebar.tpl. Do you find this style appropriate?

In this case, we need to implement different components for different languages, though the variations are minimal, for instance:

<nav id="sidebar"
  class="flex-shrink hidden leading-relaxed md:block md:sticky md:top-0 md:max-h-screen md:overflow-y-auto md:w-48 xl:w-64">
  <div class="px-2 py-2 text-gray-800">
    <div id="indexing-links" class="flex flex-row float-right p-2 space-x-2 text-gray-500">
      <a href="${ema:tagIndexUrl}" title="标签">  <!-- only changed here -->
        <svg style="width: 1rem;" class="hover:text-${theme}-700" fill="none" stroke="currentColor"
          viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
            d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z">
          </path>
        </svg>
      </a>

This approach seems somewhat verbose, do you have any alternative suggestions?

Moreover, this method cannot be applied to layouts, as the default layout location has been hardcoded at here.

name: /templates/layouts/default

srid commented

I don't think we should have per-language templates. Instead, the default layer should support translation somehow.

[I added a todo to issue description to this effect]

Perhaps we can have a strings table in Haskell that is indexed by language (page.lang yaml frontmatter), and then pass the approproriate language table to Heist as template var (see Template.hs), which then gets used in .tpl files instead of inline English text. What do you think?

So it would look like,

<a href="${ema:tagIndexUrl}" title="${strings:tagIndexTitle}"> 
brsvh commented

Yes, I share the same opinion.

It appears that for page interface elements, we can make a simple convention for looking up multilingual dictionaries. By having Haskell parse a table at a designated location to bind the variables that will be used in the templates. For example, templates/i18n/{en,fr}/translations.json:

# en

{
  "tagIndexTitle": "foo",
  ...
}

# fr

{
  "tagIndexTitle": "bar",
  ...
}

However, during static website generation, we need to pass all translations to fill in the strings. A variable may have multiple corresponding values, so how should variable bindings in the template be handled?

srid commented

@brsvh You can create a Haskell type like,

newtype TranslationTable = TranslationTable { unTranslationTable :: Map Lang (Map Text Text) }

-- | Parse translations.json from notebook
parseTranslations :: FilePath -> IO (Either Text TranslationTable)
parseTranslations = undefined 

-- | Get effective translations for given lang (falling back to English when an entry is missing)
tableFor :: TranslationTable -> Lang -> Map Text Text
tableFor = ...

Then, you store the TranslationTable in the Model type here:

data ModelT encF = Model
{ _modelStatus :: Status
, _modelLayers :: Set Loc
, _modelEmaCLIAction :: Ema.CLI.Action
, _modelRoutePrism :: encF (Prism' FilePath SiteRoute)
, _modelPandocRenderers :: EmanotePandocRenderers Model LMLRoute
-- ^ Dictates how exactly to render `Pandoc` to Heist nodes.
, _modelCompileTailwind :: Bool
, _modelInstanceID :: UUID
-- ^ An unique ID for this process's model. ID changes across processes.
, _modelNotes :: IxNote
, _modelRels :: IxRel
, _modelSData :: IxSData
, _modelStaticFiles :: IxStaticFile
, _modelTasks :: IxTask
-- ^ A tree (forest) of all notes, based on their folder hierarchy.
, _modelHeistTemplate :: TemplateState
, _modelStorkIndex :: Stork.IndexVar
, _modelFolgezettelTree :: Forest R.LMLRoute
-- ^ Folgezettel tree computed once for each update to model.
}

Then you can pass tableFor as json template var (see Template.hs), and access them from templates. Once in templates, they work the same in both live server and static site.

If you need more help, let me know.

srid commented

A variable may have multiple corresponding values, so how should variable bindings in the template be handled?

Oh, you would choose them from page.lang property (falling back to the en default). But all of this happens in Haskell (tableFor above), not the templates.