diff options
author | Thomas Voss <mail@thomasvoss.com> | 2025-07-24 20:40:48 +0200 |
---|---|---|
committer | Thomas Voss <mail@thomasvoss.com> | 2025-07-24 20:40:48 +0200 |
commit | cc151115e837836fa36cc615db8960491538ec00 (patch) | |
tree | e4a1b5d6ed20416504d7ce901d1893ae3548e238 | |
parent | 1229eac801771fd12bcc885d393ef1727c02eddc (diff) |
Add some documentation
-rw-r--r-- | README.md | 13 | ||||
-rw-r--r-- | docs/i18n.md | 135 |
2 files changed, 148 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..b2005f4 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# euro-cash.eu — The Euro Cash Wiki + +This is the Git repository for the Euro Cash Wiki. This repository +contains everything to do with the website including the backend, +translations, etc. + +All documentation is located in the `doc/` directory. + +For translators: +- [Notes When Translating Pages](#) + +For programmers: +- [The Internationalization System](docs/i18n.md)
\ No newline at end of file diff --git a/docs/i18n.md b/docs/i18n.md new file mode 100644 index 0000000..f61a0dd --- /dev/null +++ b/docs/i18n.md @@ -0,0 +1,135 @@ +# The Internationalization System + +## Extracting Translations + +For translators to be able to translate text, and for us to be able to +work with those translations, we need translation files. These files are +located in the `/po` directory. These files are automatically generated +by running the `make po` command. In order to extract translations from +the source code, the `make po` command will search for calls to the +`Get()` family of functions, such as `Get()` and `GetN()`. + +```go +func example(n int) { + /* Not extracted for translation */ + fmt.Println("Hello, Sailor!") + + /* Extracted for translation */ + fmt.Println(i18n.GetN("1 book", "{N} books", n, + map[string]any{"N": n})) +} +``` + +Sometimes you want to provide additional context about a translation to +the translators. You can do this by placing a comment above the +translated string with the prefix ‘TRANSLATORS:’. + +```go +func example() string { + /* TRANSLATORS: ‘Home’ button on the navigation bar */ + return i18n.Get("Home") +} +``` + +Especially when working in HTML templates, you may have a string that you +want to go through the formatting system but _not_ be marked for +translation. You can do this by calling the underlying `Sprintf()` +function instead. + +```html +<p>{{ .Printer.Sprintf "{N:m}" (map "N" 2) }}</p> +``` + +## The Formatting System + +For string formatting we use a custom implementation of `Sprintf()`, and +all `Get*()` functions format the translated strings using our own +`Sprintf()`. + +Do note that all `Sprintf()` output is automatically HTML escaped. + +Unlike the standard `Sprintf()`, we use named placeholders enclosed by +curly braces instead of percent codes. As placeholders are named, they +need to be passed via a map of type `map[string]any`. + +```go +func example() { + status := "late" + + /* Go’s Sprintf */ + _ = fmt.Sprintf("The bus is %s", status) + + /* Our Sprintf */ + _ = i18n.Sprintf("The bus is {LateOrEarly}", + map[string]any{"LateOrEarly": status}) +} +``` + +The result is a lot more visually-noisy than what Go does — mostly due +to the map syntax — but it offers infinitely more information to +translators, which is important. + +Translation functions can be provided multiple argument maps, and maps +can be easily created in HTML templates using the `map` function. + +```html +{{ $nlargs := (map "DutchStart" `<span lang="nl"><em>` + "DutchEnd" `em,span`) }} +<p>{{ .Get "{Name} said ‘{DutchStart:r}{Quote}{DutchEnd:E}’!" + (map "Name" "Thomas" "Quote" "...") $nlargs }}</p> +``` + +In a placeholder you can also use a colon and additional character code +to customize how formatting is performed. The default behaviour is as +follows: + +- Strings are printed verbatim +- Numbers (`int`) and floats (`float64`) are formatted with the + locale-specific grouping- and decimal separators +- Dates (`time.Time`) are formatted according to the current locale +- Other types are coerced to a string by `fmt.Sprintf()` and then printed + verbatim + +The following character codes are available with the following behaviour: + +`e` +: \[e]mail — inserts a link to an email address +`E` +: \[E]nd — inserts closing-tags for the comma-separated HTML tags +`l` +: \[l]ink (internal) — links to an internal page +`L` +: \[L]ink (external) — links to an external page +`m` +: \[m]onetary — formats the given `int` or `float64` as an amount of Euros. +`r` +: \[r]aw — inserts the given string verbatim without HTML escaping + +```html +<!-- <a href="mailto:help@euro-cash.eu">help@euro-cash.eu</a> --> +<p>{{ .Get "{Email:e}" (map "Email" "help@euro-cash.eu") }}</p> + +<!-- <span lang="sv"><em>Växjösjön</em></span> --> +<p>{{ .Get "{SwedishStart:r}Växjösjön{SwedishEnd:E}" + (map "SwedishStart" `<span lang="sv"><em>` + "SwedishEnd" "em,span") }}</p> + +<!-- Click <a href="/banknotes">here</a>! --> +<p>{{ .Get "Click {Link:l}here{-:E}!" + (map "Link" "/banknotes") }}</p> + +<!-- Click <a href="https://euro-cash.eu" target="_blank">here</a>! --> +<p>{{ .Get "Click {Link:L}here{-:E}!" + (map "Link" "https://euro-cash.eu") }}</p> + +<!-- Varies per locale --> +<!-- €1 and €1.00 --> +<p>{{ .Get "{1:m} and {1.00:m}" (map "1" 1 "1.00" 1.00) }}</p> +``` + +Some additional notes: +- The `-` name is special. `{-:E}` inserts `</a>`. This exists because + of how often you need to do this. +- The `m` character code won’t include decimals when the argument is an + integer, and will include the decimals when the argument is a + floating-point number.
\ No newline at end of file |