Bugfix Issue 1843 fr_FR strings missing 90 TLS keys - sebiboga/testlink-upgraded GitHub Wiki
Bugfix — Issue #1843: locale/fr_FR/strings.txt was missing 90 $TLS_* keys
| Issue | #1843 |
| Branch | fix/issue-1843 |
| Files changed | locale/fr_FR/strings.txt (+94 / −0), CHANGELOG (1 entry) |
| Severity | minor — cosmetic, plus 30 Event Viewer LOCALIZATION Warning rows per login |
| Regression suite | tmp/TLU_Test_Cases.md → ## Regression — Issue #1843: … (7/7 PASS) |
| Sibling | #1840 — the client gui/templates/i18n/fr.json bundle (different layer) |
Symptom
A fr_FR user sees the ASIDE menu partly in English: the Test Strategy
section header renders Test Strategy while every neighbour is French
(Tableau de bord, Système, Projets, En révision); expanding the section
shows all 28 chapter sub-items in English too. The Event Viewer (events
table) collects 30 log_level=32 / LOCALIZATION Warning rows per login,
one per missing key:
string 'title_test_strategy' is not localized for locale 'fr_FR' - using en_GB
string 'href_test_strategy_bug_lifecycle' is not localized for locale 'fr_FR' - using en_GB
...
This is the legacy lang_get() string layer (locale/*/strings.txt), not
the client-side gui/templates/i18n/*.json bundles. The two layers have two
different fallbacks: lang_get() falls back to English and logs a warning,
TLi18n.t() falls back to the raw key.
Investigation (measured before touching code)
Fresh DB (SELECT COUNT(*) FROM events = 0), app http://localhost:8082,
admin/admin, UPDATE users SET locale='fr_FR' WHERE id=1;, reproduces at
tproject_id=0.
| layer | measurement | result |
|---|---|---|
| ASIDE render | gui/templates/aside/aside.html?locale=fr_FR |
header Test Strategy English; all 28 sub-items English |
events table |
after login + ASIDE render | 31 rows = 1 LOGIN + 30 LOCALIZATION |
| browser console | list_console_messages |
no JS errors — pure translation gap |
| static key scan | en_GB vs fr_FR | en 2796 / fr 2716 / missing 90 |
Refinements over the filed report: the count is 90, not 89 (the default
branch moved since filing), and Documentation is a false positive —
api/aside/index.php:714 uses $TLS_title_documentation, already defined in fr
(:2308 = "Documentation"; the French word is spelled identically). Only
Test Strategy was a genuine fallback.
Root cause
| # | file:line | what happens |
|---|---|---|
| 1 | api/aside/index.php:402-429 (keys), :447-451 (lang_get($lblKey) loop) |
ASIDE emits 28 × lang_get('href_test_strategy_*'). |
| 2 | api/aside/index.php:456 |
plus lang_get('title_test_strategy') → Test Strategy. |
| 3 | lib/functions/lang_api.php:65-68 |
key absent from $g_lang_strings['fr_FR']. |
| 4 | lib/functions/lang_api.php:78-88 |
falls back to en_GB, returns English. |
| 5 | lib/functions/lang_api.php:122-138 |
logL18NWarningEvent(… , "LOCALIZATION") → the Warning rows. |
Regression source: 08c334b4a (Refs #1423) and f934433d7 (Refs
#1426/#1440-#1458) created the Test Strategy keys but git show --stat shows
they touched only locale/en_GB, locale/en_US, locale/ro_RO —
locale/fr_FR/strings.txt was never updated. The other 61 keys are older
en-only additions (bd0540d87 alien/platform, 34302f8ee print-doc errors,
3ec2526f8 baseline, c746eeddc ghost/relations, 1c33eb305 alien relation
types, …).
Blast radius: 90 keys in fr_FR — 30 on the ASIDE (the 29 Test Strategy keys
plus install_header, System section, api/aside/index.php:349), 60 dormant
until their path runs (printDocument errors, Alien References,
severityConfig_saved, tcView tc_has_relations, plan activate/deactivate,
tc-edit location labels, navBar toggle_navigation, …). The
href_test_strategy_* family is also missing from 15 of 19 locales (present in
en_GB, en_US, ro_RO and, after this fix, fr_FR) — out of scope for this issue.
Fix
locale/fr_FR/strings.txt — one tagged block with all 90 French
translations (fr 2716 → 2806 keys, en − fr = 0). Placeholders (%s, %i,
{%1}) preserved; $TLS_severityConfig_saved keeps its
$TLS_testproject . "…" expression ($TLS_testproject already defined at
:197); $TLS_install_header added as its own statement (fr already defines
title_product_mgmt); the two multi-line en_GB keys written single-line; UTF-8.
Rejected alternatives: suppressing the fallback warning in lang_get()
(hides genuine gaps, changes shared behaviour) and translating in
gui/templates/i18n/fr.json (that is #1840's layer — different file, different
fallback, would not fix the ASIDE).
Screenshots
Before — ASIDE header Test Strategy in English; expanding shows all 28
sub-items English:

After — header Stratégie de test, all 28 sub-items French
(Vue générale … Tests de maintenabilité, Configuration de la sévérité):

Verification
| case | before | after |
|---|---|---|
php -l locale/fr_FR/strings.txt |
— | No syntax errors detected |
static key scan en − fr |
90 | 0 (en 2796 / fr 2806) |
ASIDE header, fresh fr_FR session |
Test Strategy |
Stratégie de test |
| expand 28 sub-items | all English | all French |
events after fresh login + ASIDE + Test Strategy page + Event Viewer |
+30 log_level=32 per session |
total 33, 30 warnings (all pre-fix ids 1-31); fresh session only LOGOUT(32) + LOGIN(33) |
| files changed | — | 1 file, +94/-0 |
Regression suite Issue #1843 → 7/7 PASS; suite gate
TLU_REQUIRE_SUITE="Issue #1843" bash ai/verify_test_suites.sh → 7/7 PASS.
Re-test in one command
mysql -h 127.0.0.1 -utestlink -ptestlink testlink -e "update users set locale='fr_FR' where id=1;"
# fresh browser session, login admin/admin, open http://localhost:8082/index.php
# ASIDE header must read "Stratégie de test" (not "Test Strategy")
python3 -c "
import re
def keys(p): return set(m.group(1) for m in (re.match(r'\\\$TLS_([A-Za-z0-9_]+)\\s*=', l) for l in open(p, encoding='utf-8', errors='replace')) if m)
en=keys('locale/en_GB/strings.txt'); fr=keys('locale/fr_FR/strings.txt')
print('missing', len(en-fr))" # expected: 0
Address: Fixes #1843.