From 62cf77b09298f31d2bc9d6b1e25e011e19fede5a Mon Sep 17 00:00:00 2001 From: Thomas Voss Date: Thu, 24 Jul 2025 20:47:07 +0200 Subject: Rename docs/ to doc/ --- README.md | 2 +- doc/i18n.md | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ docs/i18n.md | 131 ----------------------------------------------------------- 3 files changed, 132 insertions(+), 132 deletions(-) create mode 100644 doc/i18n.md delete mode 100644 docs/i18n.md diff --git a/README.md b/README.md index b2005f4..17f4e4e 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,4 @@ For translators: - [Notes When Translating Pages](#) For programmers: -- [The Internationalization System](docs/i18n.md) \ No newline at end of file +- [The Internationalization System](doc/i18n.md) \ No newline at end of file diff --git a/doc/i18n.md b/doc/i18n.md new file mode 100644 index 0000000..475a512 --- /dev/null +++ b/doc/i18n.md @@ -0,0 +1,131 @@ +# 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 +

{{ .Printer.Sprintf "{N:m}" (map "N" 2) }}

+``` + +## 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" `` + "DutchEnd" `em,span`) }} +

{{ .Get "{Name} said ‘{DutchStart:r}{Quote}{DutchEnd:E}’!" + (map "Name" "Thomas" "Quote" "...") $nlargs }}

+``` + +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: + +| Code | Mnemonic | Description | +| ---- | ------------------ | ----------------------------------------------------------- | +| `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 + +

{{ .Get "{Email:e}" (map "Email" "help@euro-cash.eu") }}

+ + +

{{ .Get "{SwedishStart:r}Växjösjön{SwedishEnd:E}" + (map "SwedishStart" `` + "SwedishEnd" "em,span") }}

+ + +

{{ .Get "Click {Link:l}here{-:E}!" + (map "Link" "/banknotes") }}

+ + +

{{ .Get "Click {Link:L}here{-:E}!" + (map "Link" "https://euro-cash.eu") }}

+ + + +

{{ .Get "{1:m} and {1.00:m}" (map "1" 1 "1.00" 1.00) }}

+``` + +Some additional notes: +- The `-` name is special. `{-:E}` inserts ``. 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 diff --git a/docs/i18n.md b/docs/i18n.md deleted file mode 100644 index 475a512..0000000 --- a/docs/i18n.md +++ /dev/null @@ -1,131 +0,0 @@ -# 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 -

{{ .Printer.Sprintf "{N:m}" (map "N" 2) }}

-``` - -## 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" `` - "DutchEnd" `em,span`) }} -

{{ .Get "{Name} said ‘{DutchStart:r}{Quote}{DutchEnd:E}’!" - (map "Name" "Thomas" "Quote" "...") $nlargs }}

-``` - -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: - -| Code | Mnemonic | Description | -| ---- | ------------------ | ----------------------------------------------------------- | -| `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 - -

{{ .Get "{Email:e}" (map "Email" "help@euro-cash.eu") }}

- - -

{{ .Get "{SwedishStart:r}Växjösjön{SwedishEnd:E}" - (map "SwedishStart" `` - "SwedishEnd" "em,span") }}

- - -

{{ .Get "Click {Link:l}here{-:E}!" - (map "Link" "/banknotes") }}

- - -

{{ .Get "Click {Link:L}here{-:E}!" - (map "Link" "https://euro-cash.eu") }}

- - - -

{{ .Get "{1:m} and {1.00:m}" (map "1" 1 "1.00" 1.00) }}

-``` - -Some additional notes: -- The `-` name is special. `{-:E}` inserts ``. 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 -- cgit v1.2.3