Loading localized strings at runtime
Understand the rules for using localized strings at runtime.
Distributing compiled string files
The compiled string files (.42s) must be distributed with the program files in a directory specified in the FGLRESOURCEPATH environment variable.
Setting the correct locale
The locale (LANG
/LC_ALL
) corresponding to the encoding used in
the .42s files must be set before starting the application. If the locale is
wrong, the strings will not be loaded properly.
How does the runtime system load the strings?
The .42s compiled string resource files are loaded in the following order of precedence:
- The files defined in
FGLPROFILE
, - A file having the same name as the current program (myprog.42m loads myprog.42s),
- A file with the name "default.42s".
Each .42s string resource file is searched in several directories, as described in the FGLRESOURCEPATH reference topic.
String resource file sharing
Like .42m program pcode files, the .42s
string resource files are shared by all fglrun processes running
on the computer: The string file is loaded into memory with the
mmap
operating system function.
Defining a list of string files in FGLPROFILE
Specify a list of compiled string files with entries in the FGLPROFILE configuration file with the
fglrun.localization
entries.
fglrun.localization.file.count = integer
fglrun.localization.file.index.name = "filename.42s"
Warning switches can be specified in FGLPROFILE.
fglrun.localization.warnKeyNotFound = boolean
By default, this warning switch is disabled.
What happens if a 42s string file is not found?
If the 42s string file was defined with
fglrun.localization.*
FGLPROFILE entries, it is considered as mandatory, and the
runtime system will raise error
-8006 if the file is not found. If the progname.42s
and default.42s string files are not found, no error is raised, because these
are fallback string resource files.
What happens if a string is not defined in a resource file?
If a localized string is not defined in one of the compiled string files, the runtime system uses the string identifier as default text.
What happens if a string is defined more that once?
When a localized string is defined in several compiled string files, the runtime system uses the first string found.
For example, if the string "hello" is defined in program.42s as "hello from program", and in default.42s as "hello from default", the runtime system will use the text "hello from program".
Organizing .42s resource files in distribution directories
A set of .42s files using the same language and codeset is typically copied in a distribution directory with a name identifying the locale.
/opt/app/resource/strings/en_US.iso8859-15 -- English strings in iso8859-15 code-set
/opt/app/resource/strings/fr_FR.iso8859-15 -- French strings in iso8859-15 code-set
/opt/app/resource/strings/jp_JP.utf8 -- Japanese strings in utf-8 code-set
DBPATH
/FGLRESOURCEPATH
environment variable by adding the name of
current locale as sub-directory. For example, to find the correct string files in one of the
locale-specific directories shown above, set the FGLRESOURCEPATH
variable as
follows (UNIX™
shell):$ echo $LC_ALL
jp_JP.utf8
$ FGLRESOURCEPATH="$FGLRESOURCEPATH:/opt/app/resource/strings/$LC_ALL"
$ export FGLRESOURCEPATH
$ echo $FGLRESOURCEPATH
/opt/app/forms:/opt/app/resource/strings/jp_JP.utf8
Localized string files on mobile devices
- On iOS devices, go to
Settings >> General >> Language & Region
- On Android™ devices, go to
Settings >> General Management >> Language and Input
en
- English (for all regions)en_US
- English in the United Statesen_GB
- English in the United Kingdom
- appdir/full-locale-code: the sub-directory with language and category/region codes. For example for an English-US locale: appdir/en_US.
- appdir/language-code: the sub-directory with language code. For example for an English-US locale: appdir/en.
- appdir/defaults: the fallback directory where default string files are located.
In order to localize your application, you simply need to place your .42s localized string files in the appropriate language sub-directory.
If you want to distinguish language categories (Simplified/Tradition Chinese), or if you want to use different texts for territories that share the same language (English in USA or Great Britain), create language sub-directories with the exact OS locale identifier:
- For English in the USA, use "en_US"
- For English in the United Kingdom, use "en_GB"
- For English in Canada, use "en_CA"
- etc...
appdir/en_US/mystrings.42s
appdir/en_GB/mystrings.42s
appdir/en_CA/mystrings.42s
If the language category or region can be ignored, create language sub-directories with names matching the language identifier only:
- For English, use "en"
- For French, use "fr"
- For German, use "de"
- etc...
appdir/en/mystrings.42s
appdir/fr/mystrings.42s
appdir/de/mystrings.42s
appdir/defaults/mystrings.42s
For more details about the mobile app directory structures (appdir), see Directory structure for GMA apps and Directory structure for GMI apps.
Selecting the application language at runtime
In order to implement a typical login dialog where the end user can choose the application language, it must be possible to reset the path to find localized strings at runtime.
base.Application.reloadResources()
method. However, this method has a
limited impact. For example, localized strings of already loaded .42m modules
and .42f forms are left unchanged. Subsequent loaded forms and modules will get
localized strings depending on the new lookup path. Therefore, this method must only be called for
the initial form, when the program
starts.IMPORT os
MAIN
DEFINE done BOOLEAN
DEFINE rec RECORD
user STRING,
pswd STRING,
lang CHAR(2)
END RECORD,
path STRING
-- Login dialog with language selection
LET rec.lang = "en" -- Can be "en", "fr", "ge" ...
WHILE NOT done
LET path = os.Path.join(base.Application.getProgramDir(), rec.lang)
CALL base.Application.reloadResources(path)
OPEN FORM f FROM "main"
DISPLAY FORM f
INPUT BY NAME rec.* WITHOUT DEFAULTS
ON CHANGE lang
EXIT INPUT -- restarts the input with the new locale settings
ON ACTION cancel
EXIT PROGRAM
AFTER INPUT
LET done = TRUE
END INPUT
END WHILE
-- Here starts the real application code
...
END MAIN