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:

ASIDE fr_FR before the fix

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

ASIDE fr_FR after the fix

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.