Using resourcestrings: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: Using resourcestrings
| Old | New | Line | |
|---|---|---|---|
| 1 | + | Resource strings provide a mechanism to internationalize (and to some degree localize) your application. | |
| 2 | + | ||
| 3 | + | ## rationale | |
| 4 | + | ||
| 5 | + | Resource strings are constants with an extra level of indirection. The default value (as written in your [[Source code|source code]]) is stored in the executable program. That means it is always available, even if loading the translation fails. | |
| 6 | + | ||
| 7 | + | ## common steps | |
| 8 | + | ||
| 9 | + | The first step is to *use* resource strings for every string that potentially needs translation. In this tutorial we want to internationalize our Hello World program: | |
| 10 | + | ||
| 11 | + | ```delphi | |
| 12 | + | program helloWorld(input, output, stdErr); | |
| 13 | + | resourceString | |
| 14 | + | hello = 'Hello world'; | |
| 15 | + | begin | |
| 16 | + | writeLn(hello); | |
| 17 | + | end. | |
| 18 | + | ``` | |
| 19 | + | ||
| 20 | + | The [[FPC]] creates a file ending in `.rsj` (resource string [[JSON]]) for every compiled module containing a `resourceString` section. For the above program it looks like this (pretty-format added): | |
| 21 | + | ||
| 22 | + | ``` json | |
| 23 | + | { | |
| 24 | + | "version": 1, | |
| 25 | + | "strings": [ | |
| 26 | + | { | |
| 27 | + | "hash": 20229140, | |
| 28 | + | "name": "helloworld.hello", | |
| 29 | + | "sourcebytes": [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100], | |
| 30 | + | "value": "Hello world" | |
| 31 | + | } | |
| 32 | + | ] | |
| 33 | + | } | |
| 34 | + | ``` | |
| 35 | + | ||
| 36 | + | This intermediate file format was created, so the compiler does not need any knowledge of the utilized translation tool. It just needs to create `.rsj` files. | |
| 37 | + | ||
| 38 | + | The following steps differ on the used internationalization mechanism. As of 2021 the FPC only supports [[GNU]] gettext entirely. The reason gettext was chosen is that it is more or less standard on Unix-like systems, like [[Linux]] or [[FreeBSD]]. | |
| 39 | + | ||
| 40 | + | ## GNU get text | |
| 41 | + | ||
| 42 | + | ### limitations | |
| 43 | + | ||
| 44 | + | As mentioned, GNU gettext is widely available on Unix-like systems. However, gettext is horribly inefficient. Also, gettext is context insensitive, that means it operates on the string itself: Sometimes the same word/sentence must be translated differently according to the context, but this is not possible. | |
| 45 | + | ||
| 46 | + | ### procedure | |
| 47 | + | ||
| 48 | + | 1. Convert the `.rsj` file into GNU gettext’s portable object format: | |
| 49 | + | ``` bash | |
| 50 | + | rstconv -f po -i helloWorld.rsj -o helloWorld.po | |
| 51 | + | ``` | |
| 52 | + | 2. Create a working copy of the generated file, e. g. | |
| 53 | + | ``` bash | |
| 54 | + | cp helloWorld.po helloWorld-de.po | |
| 55 | + | ``` | |
| 56 | + | ||
| 57 | + | and translate it with your favorite translation tool or simply with a text editor. | |
| 58 | + | ||
| 59 | + | msgid "Hello world" msgstr "Hallo Welt" | |
| 60 | + | ||
| 61 | + | 1. Convert the PO file to an MO (machine object) file using gettext’s utility `msgfmt`: | |
| 62 | + | ``` bash | |
| 63 | + | msgfmt -o helloWorld-de.mo helloWorld-de.po | |
| 64 | + | ``` | |
| 65 | + | 2. Integrate gettext in your program: | |
| 66 | + | ||
| 67 | + | uses | |
| 68 | + | ||
| 69 | + | ` gettext;` | |
| 70 | + | ||
| 71 | + | resourceString | |
| 72 | + | ||
| 73 | + | ` hello = 'Hello world';` | |
| 74 | + | ||
| 75 | + | begin | |
| 76 | + | ||
| 77 | + | ` translateResourceStrings('helloWorld-%s.mo');`\ | |
| 78 | + | ` writeLn(hello);` | |
| 79 | + | ||
| 80 | + | end. | |
| 81 | + | ||
| 82 | + | The `%s` is substituted with the locale’s name. Examining an `strace` you will see, that for example first `helloWorld‑de.mo` is attempted to open. If it does not exist, it tries `helloWorld‑de_DE.mo`. If neither file exists, the resource string’s default values as specified in your source code are used. | |
| 83 | + | ||
| 84 | + | 1. Run the program and celebrate your achievement: | |
| 85 | + | ``` bash | |
| 86 | + | LANG='de_DE.UTF-8' ./helloWorld | |
| 87 | + | ``` | |
| 88 | + | ||
| 89 | + | For more details see the [gettext unit documentation](https://www.freepascal.org/docs-html/fcl/gettext/index.html). | |
| 90 | + | ||
| 91 | + | ## resource DLLs | |
| 92 | + | ||
| 93 | + | The FPC’s standard RTL does not yet contain portable functions for loading texts from resource [[DLL|DLLs]] containing translations (as [[Delphi]] does), but the first step would be: | |
| 94 | + | ||
| 95 | + | 1. Convert the `.rsj` file into a resource compiler script: | |
| 96 | + | ``` bash | |
| 97 | + | rstconv -f rc -i helloWorld.rsj -o helloWorld.rc | |
| 98 | + | ``` | |
| 99 | + | ||
| 100 | + | ## message files | |
| 101 | + | ||
| 102 | + | The FPC currently does not support this, but the first step would be: | |
| 103 | + | ||
| 104 | + | 1. Convert the `.rsj` file in an IBM OS/2 message file: | |
| 105 | + | ``` bash | |
| 106 | + | rstconv -f msg -c HWX -i helloWorld.rsj -o helloWorld.msg | |
| 107 | + | ``` | |
| 108 | + | ||
| 109 | + | The component identifier indicated by `‑c` may be any three upper-case [[ASCII]] letters, but it should be unique within your project. | |
| 110 | + | ||
| 111 | + | ## alternatives | |
| 112 | + | ||
| 113 | + | To implement another mechanism, 3 things are needed: | |
| 114 | + | ||
| 115 | + | 1. Update rstconv so it can output another format. | |
| 116 | + | 2. Tools to manipulate the other format. | |
| 117 | + | 3. Implement a unit that loads the other format at [[runtime|run-time]]. | |
| 118 | + | ||
| 119 | + | [[Routine|Routines]] to directly manipulate a program’s resource string tables, i. e. the extra level of indirection for resource strings, were available in the objpas unit [until revision 31687](https://gitlab.com/freepascal.org/fpc/source//commit/4494565a154b1c6b313c9ddc3a09f7102719c5b3). | |
| 120 | + | ||
| 121 | + | ## history | |
| 122 | + | ||
| 123 | + | Formerly, the FPC created text files ending in `.rst`. While changing to the JSON format, the name of the [`rstconv` utility](https://www.freepascal.org/docs-html/user/userse45.html) was preserved. `rstconv` can still convert `.rst` files. | |
| 124 | + | ||
| 125 | + | ```text | |
| 126 | + | # hash value = 20229140 | |
| 127 | + | helloworld.hello='Hello world' | |
| 128 | + | ``` | |
| 129 | + | ||
| 130 | + | ## see also | |
| 131 | + | ||
| 132 | + | - [[Step-by-step instructions for creating multi-language applications]] | |
| 133 | + | ||
| 134 | + | --- | |
| 135 | + | *From the Free Pascal wiki, page "Using resourcestrings", as it stood on 2022-01-25 after a change by Kai Burghardt, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* | |
| 136 | + | Category: Tutorials |
