Format string not formatted when administrating a user #2551

Closed
opened 2025-11-02 04:40:07 -06:00 by GiteaMirror · 9 comments
Owner

Originally created by @vbrandl on GitHub (Nov 20, 2018).

Description

When changing a user in the admin area and saving the changes, at least when using the german language pack, the UI will confirm the changes with Der Account „%s“ wurde aktualisiert. (The account has been updated). I could not test it on try.gitea.io since I cannot the admin panel. I also did not test if this is true for other languages, but it looks like a format string is not formatted.

Screenshots

screenshot

Originally created by @vbrandl on GitHub (Nov 20, 2018). <!-- 1. Please speak English, this is the language all of us can speak and write. 2. Please ask questions or configuration/deploy problems on our Discord server (https://discord.gg/NsatcWJ) or forum (https://discourse.gitea.io). 3. Please take a moment to check that your issue doesn't already exist. 4. Please give all relevant information below for bug reports, because incomplete details will be handled as an invalid report. --> - Gitea version (or commit ref): 2a6d3ba - Can you reproduce the bug at https://try.gitea.io: - [ ] Yes (provide example URL) - [ ] No - [ ] Not relevant ## Description When changing a user in the admin area and saving the changes, at least when using the german language pack, the UI will confirm the changes with `Der Account „%s“ wurde aktualisiert.` (The account has been updated). I could not test it on try.gitea.io since I cannot the admin panel. I also did not test if this is true for other languages, but it looks like a format string is not formatted. ## Screenshots ![screenshot](https://user-images.githubusercontent.com/20639051/48799989-c676dd00-ed08-11e8-9428-e0d541ff2d3b.png) <!-- **If this issue involves the Web Interface, please include a screenshot** -->
GiteaMirror added the issue/stalemodifies/translation labels 2025-11-02 04:40:07 -06:00
Author
Owner

@lunny commented on GitHub (Nov 21, 2018):

I think this maybe a translation problem. Could you try english version UI?

@lunny commented on GitHub (Nov 21, 2018): I think this maybe a translation problem. Could you try english version UI?
Author
Owner

@vbrandl commented on GitHub (Nov 21, 2018):

You are right. The English message does not contain the username: b97af15de6/options/locale/locale_en-US.ini (L1416)
It's the key users.update_profile_success in the German language bundle, that is wrong. I will create a PR for this later

@vbrandl commented on GitHub (Nov 21, 2018): You are right. The English message does not contain the username: https://github.com/go-gitea/gitea/blob/b97af15de67b04fd259bd70a4abbc873f12e9491/options/locale/locale_en-US.ini#L1416 It's the key `users.update_profile_success` in the German language bundle, that is wrong. I will create a PR for this later
Author
Owner

@bugreport0 commented on GitHub (Nov 21, 2018):

We could create a unit test that compares the format-string 'signature' of translations to make sure translations line up correctly. The signature is just the percent-signs with formats, like:

  • Added user %s has signature %s
  • Step %u of %u complete for user %s has signature %u%u%s

We can then:

  • read the English translation 'truth' mapping string_key to signature
  • read other translations to check signature matches for string_key

I used this on a different project and it's worked great to catch errors.

@bugreport0 commented on GitHub (Nov 21, 2018): We could create a unit test that compares the format-string 'signature' of translations to make sure translations line up correctly. The signature is just the percent-signs with formats, like: - `Added user %s` has signature `%s` - `Step %u of %u complete for user %s` has signature `%u%u%s` We can then: - read the English translation 'truth' mapping `string_key` to `signature` - read other translations to check `signature` matches for `string_key` I used this on a different project and it's worked great to catch errors.
Author
Owner

@lunny commented on GitHub (Nov 21, 2018):

@bugreport0 Good idea. Could you send a PR to do that? And but currently our translation will be committed by a robot automatically.

@lunny commented on GitHub (Nov 21, 2018): @bugreport0 Good idea. Could you send a PR to do that? And but currently our translation will be committed by a robot automatically.
Author
Owner

@vbrandl commented on GitHub (Nov 21, 2018):

From the format of the used string, I guess, gitea does not use named placeholder in format strings, but take this (admittedly conceived) example: User %s visited %d minutes ago vs Vor %d Minuten besuchte der Nutzer %s diese Seite. What I'm trying to say is, that the order of the placeholders might change depending on the language.

The first one would have the signature %s%d while the second one would be %d%s. Maybe the signature should be a hashmap, mapping the format string to the number of occurrences of the pattern. For the above example, this would be { "%s" => 1; "%d" => 1 } in both cases.

@vbrandl commented on GitHub (Nov 21, 2018): From the format of the used string, I guess, gitea does not use named placeholder in format strings, but take this (admittedly conceived) example: `User %s visited %d minutes ago` vs `Vor %d Minuten besuchte der Nutzer %s diese Seite`. What I'm trying to say is, that the order of the placeholders might change depending on the language. The first one would have the signature `%s%d` while the second one would be `%d%s`. Maybe the signature should be a hashmap, mapping the format string to the number of occurrences of the pattern. For the above example, this would be `{ "%s" => 1; "%d" => 1 }` in both cases.
Author
Owner

@bugreport0 commented on GitHub (Nov 22, 2018):

@vbrandl : does reordering parameters actually work in Go/Gitea? In C you get pointer values printed for const char* strings going into %d or worse. I ask translators to come up with sentences having the English parameter order.

@lunny : I'm not skilled enough in Go to do a PR. But here's the PHP code I use for inspiration. $localizedStringMap is an array mapping string identifiers to a LocalizedString. A LocalizedString maps language codes to the actual UTF-8 string.

The code loops through all strings, makes sure an English string exists, calculates the format-signature for the English string (only %s, %u and %d are supported) and then checks if all other translations have the same signature. The signature-calculation (preg_match_all) is pulled-up and duplicated here for clarity as you definitely want to have that in a separate function.

// loop through all strings
foreach ($localizedStringMap as $identifier => $localizedString) {
    $translations = $localizedString->getTranslations();

    // calculate English string signature
    assert(array_key_exists('en_US', $translations));
    $englishString = $translations['en_US'];
    $dataPlaceholderSignature = '';
    $matches = array();
    if (0 !== preg_match_all('/(%[dsu])/', $englishString, $matches)) {
        foreach ($matches[1] as $dataPlaceholder) {
            // add data format parameter to signature
            $dataPlaceholderSignature .= $dataPlaceholder;
        }
    }
    
    // check all translations
    foreach ($translations as $locale => $string) {
        if ('' != $dataPlaceholderSignature) {
            // calculate translation parameter signature
            $translatedPlaceholderSignature = '';
            $matches = array();
            preg_match_all('/(%[dsu])/', $string, $matches);
            foreach ($matches[1] as $dataPlaceholder) {
                $translatedPlaceholderSignature .= $dataPlaceholder;
            }
            
            // make sure signature matches
            if ($dataPlaceholderSignature != $translatedPlaceholderSignature) {
                printf("Data placeholder mismatch '%s' -> '%s' in [%s] '%s' (%s)",
                    $dataPlaceholderSignature, $translatedPlaceholderSignature,
                    $locale, $string, $englishString);
            }
        }
    }
}
@bugreport0 commented on GitHub (Nov 22, 2018): @vbrandl : does reordering parameters actually work in Go/Gitea? In C you get pointer values printed for `const char*` strings going into `%d` or worse. I ask translators to come up with sentences having the English parameter order. @lunny : I'm not skilled enough in Go to do a PR. But here's the PHP code I use for inspiration. `$localizedStringMap` is an array mapping string identifiers to a `LocalizedString`. A `LocalizedString` maps language codes to the actual UTF-8 string. The code loops through all strings, makes sure an English string exists, calculates the format-signature for the English string (only `%s`, `%u` and `%d` are supported) and then checks if all other translations have the same signature. The signature-calculation (`preg_match_all`) is pulled-up and duplicated here for clarity as you definitely want to have that in a separate function. ```php // loop through all strings foreach ($localizedStringMap as $identifier => $localizedString) { $translations = $localizedString->getTranslations(); // calculate English string signature assert(array_key_exists('en_US', $translations)); $englishString = $translations['en_US']; $dataPlaceholderSignature = ''; $matches = array(); if (0 !== preg_match_all('/(%[dsu])/', $englishString, $matches)) { foreach ($matches[1] as $dataPlaceholder) { // add data format parameter to signature $dataPlaceholderSignature .= $dataPlaceholder; } } // check all translations foreach ($translations as $locale => $string) { if ('' != $dataPlaceholderSignature) { // calculate translation parameter signature $translatedPlaceholderSignature = ''; $matches = array(); preg_match_all('/(%[dsu])/', $string, $matches); foreach ($matches[1] as $dataPlaceholder) { $translatedPlaceholderSignature .= $dataPlaceholder; } // make sure signature matches if ($dataPlaceholderSignature != $translatedPlaceholderSignature) { printf("Data placeholder mismatch '%s' -> '%s' in [%s] '%s' (%s)", $dataPlaceholderSignature, $translatedPlaceholderSignature, $locale, $string, $englishString); } } } } ```
Author
Owner

@lafriks commented on GitHub (Nov 22, 2018):

@bugreport0 un GoLang you can reorder with such syntax: %#[1]s

@lafriks commented on GitHub (Nov 22, 2018): @bugreport0 un GoLang you can reorder with such syntax: `%#[1]s`
Author
Owner

@stale[bot] commented on GitHub (Jan 22, 2019):

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs during the next 2 weeks. Thank you for your contributions.

@stale[bot] commented on GitHub (Jan 22, 2019): This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs during the next 2 weeks. Thank you for your contributions.
Author
Owner

@stale[bot] commented on GitHub (Feb 18, 2019):

This issue has been automatically closed because of inactivity. You can re-open it if needed.

@stale[bot] commented on GitHub (Feb 18, 2019): This issue has been automatically closed because of inactivity. You can re-open it if needed.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/gitea#2551