Groups | Search | Server Info | Login | Register


Groups > comp.arch.embedded > #32337

Re: How to add the second (or other) languages

From pozz <pozzugno@gmail.com>
Newsgroups comp.arch.embedded
Subject Re: How to add the second (or other) languages
Date 2025-02-16 19:59 +0100
Organization A noiseless patient Spider
Message-ID <votcl3$nc20$1@dont-email.me> (permalink)
References <voii3i$28jmm$1@dont-email.me> <voioe3.598.1@stefan.msgid.phost.de> <voiu1q$2f5ap$1@dont-email.me>

Show all headers | View raw


Il 12/02/2025 20:50, David Brown ha scritto:
> On 12/02/2025 18:14, Stefan Reuther wrote:
>> Am 12.02.2025 um 17:26 schrieb pozz:
>>> #if LANGUAGE_ITALIAN
>>> #  define STRING123            "Evento %d: accensione"
>>> #elif LANGUAGE_ENGLISH
>>> #  define STRING123            "Event %d: power up"
>>> #endif
>> [...]
>>> Another approach is giving the user the possibility to change the
>>> language at runtime, maybe with an option on the display. In some cases,
>>> I have enough memory to store all the strings in all languages.
>>
>> Put the strings into a structure.
>>
>>    struct Strings {
>>        const char* power_up_message;
>>    };
>>
>> I hate global variables, so I pass a pointer to the structure to every
>> function that needs it (but of course you can also make a global 
>> variable).
>>
>> Then, on language change, just point your structure pointer elsewhere,
>> or load the strings from secondary storage.
>>
>> One disadvantage is that this loses you the compiler warnings for
>> mismatching printf specifiers.
>>
>>> I know there are many possible solutions, but I'd like to know some
>>> suggestions from you. For example, it could be nice if there was some
>>> tool that automatically extracts all the strings used in the source code
>>> and helps managing more languages.
>>
>> There's packages like gettext. You tag your strings as
>> 'printf(_("Event %d"), e)', and the 'xgettext' command will extract them
>> all into a .po file. Other tools help you manage these files (e.g.
>> 'msgmerge'; Emacs 'po-mode'), and gcc knows how to do proper printf
>> warnings.
>>
>> The .po file is a mapping from English to Whateverish strings. So you
>> would convert that into some space-efficient resource file, and
>> implement the '_' macro/function to perform the mapping. The
>> disadvantage is that this takes lot of memory because your app needs to
>> have both the English and the translated strings in memory. But unless
>> you also use a fancy preprocessor that translates your code to
>> 'printf(getstring(STR123), e)', I don't see how to avoid that. In C++20,
>> you might come up with some compile-time hashing...
>>
>> I wouldn't use that on a microcontroller, but it's nice for desktop apps.
>>
>>
>>    Stefan
> 
> 
> You don't need a very fancy pre-processor to handle this yourself, if 
> you are happy to make a few changes to the code.  Have your code use 
> something like :
> 
> #define DisplayPrintf(id, desc, args...) \
>      display_printf(strings[language][string_ ## id], ## x)
> 
> Use it like :
> 
>      DisplayPrintf(event_type_on, "Event on", ev->idx);
> 
> 
> A little Python preprocessor script can chew through all your C files 
> and identify each call to "DisplayPrintf".  

Little... yes, it would be little, but not simple, at least for me. How 
to write a correct C preprocessor in Python?

This preprocessor should ingest a C source file after it is preprocessed 
by the standard C preprocessor for the specific build you are doing.

For example, you could have a C source file that contains:

#if BUILD == BUILD_FULL
   DisplayPrintf(msg, "Press (1) for simple process, (2) for advanced 
process");
   x = wait_keypress();
   if (x == '1') do_simple();
   if (x == '2') do_adv();
#elif BUILD == BUILD_LIGHT
   do_simple();
#endif

If I'm building the project as BUILD_FULL, there's at least one 
additional string to translate.

Another big problem is the Python preprocessor should understand C 
syntax; it shouldn't simply search for DisplayPrintf occurrences.
For example:

/* DisplayPrintf(old_string, "This is an old message"); */
DisplayPrintf(new_string, "This is a new message");

Of course, only one string is present in the source file, but it's not 
simple to extract it.


> It can collect together all 
> the id's and generate a header with something like :
> 
>      typedef enum {
>          string_event_type_on, ...
>      } string_index;
>      enum { no_of_strings = ... };
> 
>      enum {
>          lang_English, lang_Italian, ...
>      } language_index;
>      enum { no_of_languages = ... };
> 
>      extern language_index language;        // global var :-)
>      extern const char* strings[no_of_languages][no_of_strings];
> 
> Then it will have a C file :
> 
>      #include "language.h"
> 
>      language_index language;
>      const char* strings[no_of_languages][no_of_strings] = {
>      {    // English
>          "Event %d: power up",        // Event on
>          ...
>      }
>      {    // Italian
>          "Evento %d: accensione",    // Event on
>      }
>      }
> 
> It would generate the strings based on language files:
> 
>      # english.txt
>      event_type_on : Event %d: power up
>      ...
> 
> If the preprocessor finds a use of DisplayPrintf where the id (which can 
> be as long or short as you want, but can't have spaces or awkward 
> characters) does not match the description, it should give an error - 
> duplicate uses of the same pair are skipped.  (You could just use an id 
> and no description if you prefer.)
> 
> Any ids that are not in the language files will be printed out or put in 
> a file, ids that are in the language files but not used in the program 
> will give warnings, etc.
> 
> It can all be done in a manner that makes it easy to get right, hard to 
> get wrong, and will not cause trouble as strings are added or removed.
> 
> It would be a lot simpler than gettext, and use minimal runtime space 
> and time.  And it should be straightforward to change if you want to 
> have string tables stored externally or something like that.  (I've made 
> systems with string tables in an external serial eprom, for example.)

Thanks for the suggestion, the idea is great. However I'm not able to 
write a Python preprocessor that works well.

Back to comp.arch.embedded | Previous | NextPrevious in thread | Next in thread | Find similar


Thread

How to add the second (or other) languages pozz <pozzugno@gmail.com> - 2025-02-12 17:26 +0100
  Re: How to add the second (or other) languages Stefan Reuther <stefan.news@arcor.de> - 2025-02-12 18:14 +0100
    Re: How to add the second (or other) languages David Brown <david.brown@hesbynett.no> - 2025-02-12 20:50 +0100
      Re: How to add the second (or other) languages pozz <pozzugno@gmail.com> - 2025-02-16 19:59 +0100
        Re: How to add the second (or other) languages David Brown <david.brown@hesbynett.no> - 2025-02-17 09:51 +0100
          Re: How to add the second (or other) languages pozz <pozzugno@gmail.com> - 2025-02-17 16:05 +0100
            Re: How to add the second (or other) languages David Brown <david.brown@hesbynett.no> - 2025-02-17 19:09 +0100
      Re: How to add the second (or other) languages pozz <pozzugno@gmail.com> - 2025-02-16 22:56 +0100
        Re: How to add the second (or other) languages David Brown <david.brown@hesbynett.no> - 2025-02-17 09:57 +0100
    Re: How to add the second (or other) languages pozz <pozzugno@gmail.com> - 2025-02-16 23:15 +0100
      Re: How to add the second (or other) languages David Brown <david.brown@hesbynett.no> - 2025-02-17 09:59 +0100
      Re: How to add the second (or other) languages Stefan Reuther <stefan.news@arcor.de> - 2025-02-17 19:00 +0100
  Re: How to add the second (or other) languages "Niocláiſín Cóilín de Ġloſtéir" <Master_Fontaine_is_dishonest@Strand_in_London.Gov.UK> - 2025-02-13 22:51 +0100

csiph-web