Localization
i18n plugin for Luasmith provides tools to create multi-language websites. It uses Lettext, a Gettext implementation for Lua, allowing you to translate strings in your website templates utilizing a commonly used PO file format. The plugin automatically extracts strings and generates PO(T) files when building the website, so you don't have to have standard Gettext utilities (such as xgettext and msgmerge) being installed — everything is handled by Lettext and the plugin to simplify creation of multi-language websites.
The plugin is unfinished. While the core things are complete, there are still some important missing parts. Breaking changes are expected.
Content structure
You can use either lang field in front-matter or add a filename suffix to set the language of a page. A suffix is useful when you want some pages to be translations to each other, and the plugin will be aware of that, so you will be able to create a language switcher (like you can see one on the top of this page).
Assuming we have this content directory structure:
content/
| index.md
| index.ru.md
| some_page.md
With some_page.md having lang = ru, this structure will be produced on output:
out/
| index.html
| ru/
| index.html
| some_page.md
Changing the links in the content of pages is done by the plugin, so you should write relative links' paths as they are in your content folder and there's no need to be bothered of the fact that the path to root will change.
Pipeline
There are just 2 things that you need to call in your pipeline for your content to be localized.
i18n.localize(defaultLang, potAsDefault)This node walks over all items, changing their paths and relative links in the content as needed; it will also set required plugin-specific item fields.
defaultLangis the language code for the default language of your website. Pages withoutlangfield set and with no language code in file suffix are assumed to be pages in the default language. IfdefaultLangis nil, it will be set toen.potAsDefaultdefines whether the generatedmessages.potfile should be used as a fallback for strings translations for the default language. If the default language has multiple plural forms, it may be insufficient to use onlymsgidandmsgid_pluralto provide proper strings; ifpotAsDefaultis true, you can addmsgstrin the POT file to workaround that and the plugin will read proper strings, but that may not feel right to write "translations" into the POT file (which is the PO Template file), in such case you may want to setpotAsDefaultto false and then a separate PO file for your default language will be generated, allowing you to keep the POT file clean. IfpotAsDefaultis nil, it will be set to true.If you use
i18nplugin together with some other plugins that modify items' paths (such asanyslugorprettyurls) theni18n.localizemust be called before other plugins' nodes to avoid losing filename suffixes with language codes.i18n.writeTranslations()This node writes all translation files into
podirectory. A PO file will be generated for each language that the plugin will encounter, so that you don't need to edit LINGUAS and/or create PO files yourself. This node should be called after applying all your template and doing other stuff that calls translation functions so that all strings will be collected.
There's also i18n.setLang(path, lang) currently available as a workaround for missing functionality to set the language and add some extra stuff to pipeline-generated pages (i.e. not generated from content files). This node will most likely be removed in the future.
Usage in templates
Most of the functionality of the plugin for templates can be inspected in the code of the language switcher on this page:
<% if nlangs > 1 then -%>
<div><%= _'Languages' .. ':' -%>
<% for l, p in iterLangs() do
if l == lang then -%>
<b><%= getLangName(l) %></b>
<% else -%>
<a href="<%= p %>"><%= getLangName(l) %></a>
<% end -%>
<% end -%>
</div>
<% end -%>
nlangs is the count of languages the page is available in. This page in pacticular has nlangs being 2. The field is set automatically, don't overwrite it yourself.
_'Languages' is a translatable string. If you have ever written a code using Gettext, then you may be familiar with _(...), _p(...), _n(...) and _np(...) — these are translation function with slightly different options, but the main thing is the same: a string will be replaced by its translation from a messages catalog for the current language (or the original string is returned if there's no translation). See Lettext demo for detailed usage examples of these functions.
If you will look at messages.pot file in the source code of this website, an entry with the string is there:
msgid "Languages"
msgstr ""
iterLangs() iterates over all languages for the page, returning a language code and a path to the page in another language. The iterator will go over the current page itself, this can be detected by comparing returned language code with the language code of the current page (lang).
getLangName(lang) returns the name of the language for the provided code. Currently it returns the name in English, but this will change in the future. So, now it returns "English" for en and "Russian" for ru no matter what the current page's language is.
Now, one more thing that you can use in templates:
getLangRoot() returns a path to the language root directory for the current page.
Planned features
- A function to get date and time in localized format;
- An option to create subdirectory for the default language (so you can have a language selection page on
index.htmland everything else will be in subdirectories, similar to what Wikipedia does, except it does it on subdomains, not subdirectories); getLangNamereturning localized language names;- An easy way to create localized indexes and other pipeline-generated pages.