Repository navigation
feat(i18n): add a French translation - #819
Conversation
Adds src/assets/i18n/fr.json covering every key in en.json, in banking French (epargne, parts sociales, ecritures comptables, plan comptable, agence). Placeholders are unchanged. French is registered beside hi and ko in the language lists (app, login page, header) and in the date locale map. English stays the default. The money verbs that differ in the ledger (cancel, undo, reverse, waive, write off, charge off, chargeback) are listed in DOCS/CI_CHECKS.md so a translator keeps them distinct. The coverage baseline gains fr.
E2E — mocked backend🎭 E2E Tests✅ All green — 427 passed · 0 failed · 1 skipped, across 40 spec files in 21m 57s. By spec file
All 428 tests — click to expand
Slowest 10 — what the shard counts should be tuned against
📼 Download the HTML report, videos and traces — see the Generated by run 39254703759 from |
E2E — real Fineract🎭 E2E Tests✅ All green — 171 passed · 0 failed · 0 skipped, across 50 spec files in 20m 21s. By spec file
All 171 tests — click to expand
Slowest 10 — what the shard counts should be tuned against
📼 Download the HTML report, videos and traces — see the Generated by run 39254703759 from |
Aman-Mittal
left a comment
There was a problem hiding this comment.
lgtm and thanks for contribution
🔄 What this change talks toFrom the 4 application TypeScript file(s) changed in this pull request. sequenceDiagram
autonumber
participant AppComponent as AppComponent (Component)
participant I18N as I18N (Injected)
participant LoginComponent as LoginComponent (Component)
participant BrandingService as BrandingService (Injected)
participant ThemeService as ThemeService (Injected)
participant ConfigService as ConfigService (Injected)
participant FormBuilder as FormBuilder (Injected)
participant AuthService as AuthService (Injected)
participant NotificationService as NotificationService (Injected)
participant Router as Router (Injected)
participant HeaderComponent as HeaderComponent (Component)
participant NavigationConfigService as NavigationConfigService (Injected)
AppComponent->>I18N: registerLangs()
AppComponent->>I18N: setFallbackLang()
AppComponent->>I18N: detectBrowserLang()
AppComponent->>I18N: use()
LoginComponent->>BrandingService: appName()
LoginComponent->>ThemeService: isDarkMode()
LoginComponent->>BrandingService: logoDarkUrl()
LoginComponent->>BrandingService: logoUrl()
LoginComponent->>BrandingService: resolveLogo()
LoginComponent->>ConfigService: config()
LoginComponent->>FormBuilder: group()
LoginComponent->>AuthService: currentTenantId()
LoginComponent->>I18N: use()
LoginComponent->>NotificationService: show()
LoginComponent->>I18N: translate()
LoginComponent->>ConfigService: setApiUrl()
LoginComponent->>AuthService: login()
LoginComponent->>AuthService: twoFactorPending()
LoginComponent->>Router: navigate()
HeaderComponent->>BrandingService: appName()
HeaderComponent->>BrandingService: logoDarkUrl()
HeaderComponent->>BrandingService: logoUrl()
HeaderComponent->>BrandingService: resolveLogo()
HeaderComponent->>NavigationConfigService: searchRoutes()
HeaderComponent->>Router: navigateByUrl()
HeaderComponent->>Router: navigate()
HeaderComponent->>I18N: use()
HeaderComponent->>AuthService: logout()
Truncated for readability: 5 further participant(s) not shown.
Generated by run 39254703768 from |
`main` has been red on Translation Check since apache#819 merged, and neither apache#819 nor the change it landed beside was wrong on its own — both were green. apache#816 renamed `ON_HOLD_TRANSACTIONS.REASON` to `LOAN`, because the column shows the loan the funds are held against and never held a reason. apache#819 was authored before that and translated the key as it then stood. Git merged the two cleanly: they touch different keys in different files, so there was nothing to conflict. The result is a catalogue that disagrees with `en.json` in both directions at once: fr.json defines ON_HOLD_TRANSACTIONS.REASON, which en.json does not — remove it. fr.json covers 2746 keys, down from 2747. So `Motif` becomes `Prêt` — a retranslation rather than a rename, since the column means something else now. The same merge left the sixteen keys from apache#817's password and access work untranslated, for the same reason: that branch predated `fr.json` existing, so there was no French file for it to add them to. They are translated here, which takes `fr` to 2763/2763, and the coverage baseline is refreshed so the ratchet holds the gain rather than the old floor. `app.language.fr` is added to `hi.json` too. apache#819 registered French in the language pickers and gave `en` and `fr` a label for it, so a Hindi user was offered the new language under a raw translation key.
Unit Tests and two of the three real-Fineract E2E shards have been failing on `main`, and
not because of anything that merged. They fail on the clock. Every run started before
17:45 UTC is green; both runs started after 18:26 UTC are red, with the same four jobs:
19:07Z apache#817 merge 4 failures
18:26Z apache#819 merge 4 failures
15:02Z all green
14:23Z apache#816 merge all green
12:46Z all green
`Asia/Kolkata` is UTC+5:30, so its date turns over at 18:30 UTC. `AppConfig.tenantTimezone`
defaults to it, which is the zone the dialog resolves today in — deliberately, because the
platform validates `activate` against a date it stamped in the tenant's zone, not the
browser's. Two cases here asserted `component.actionDate()` against
`toIsoDate(new Date())`, the browser's today. For five and a half hours each evening those
are different days, the component answers the tenant's and the assertion wants the
browser's, and the job goes red:
AssertionError: expected '2026-10-20' to be '2026-10-21'
The spec's own header claimed these cases "hold whatever time it is". They now do, by
pinning the platform's clock through `recordServerTime` instead of reading the real one.
It is pinned inside the window that was breaking them — 20:00Z on the 15th, already the
16th in `Asia/Kolkata` — so the cases assert the tenant's date against a browser that
would say the 15th, which is the disagreement they exist for. Verified green at
`Asia/Kolkata`, `UTC`, `America/New_York`, `Etc/GMT-10`, `Etc/GMT+12` and
`Pacific/Kiritimati`, and failing 4 of 5 when the component is reverted to the browser
clock — before this it caught that regression only between 18:30 and midnight UTC.
The relative dates move with it: `offsetFromToday` stepped the browser's clock with local
getters, so the floor cases drifted on the same boundary. They are ISO arithmetic from the
pinned date now, anchored at midday UTC so no offset can shift them.
The two E2E shards are the same disagreement — shard 1 holds `group-membership.spec.ts`
and shard 3 `tenant-date-defaults-backend.spec.ts`, both written for this window — but
they are not addressed here. Reproducing them needs a live platform inside the window, and
a guess at an E2E spec is not worth making.
What and why
Adds a French translation,
src/assets/i18n/fr.json, covering every key inen.json(2747), with placeholders unchanged. French is registered in the language picker the same wayhiandkoare (app, login page, header) and in the date locale map. English remains the default and nothing else changes. The i18n coverage baseline gainsfr.Money verbs that differ in the ledger are kept distinct. The glossary is added to
DOCS/CI_CHECKS.md(the i18n section) for future translators:Independent of the OIDC sign-in work (#370): it carries the existing
login.sso.notImplementedstring. Whichever PR lands second adds the newlogin.sso.*keys tofr.json.Verification
npm ci,npm run lint(0 errors, baseline unchanged),npm run test:unit(310 files, 2487 tests),npm run build,npm run i18n:check(fr 2747/2747) andformat:checkon the changed files all pass.--project=mocked e2e/login.spec.ts e2e/oidc-login.spec.ts) passes. Translations were checked key by key for placeholders; no real backend was used because only string catalogues and the language lists changed.Screenshots
Available on request.
Checklist
src/app/api/.src/app/core/adapters/instead of direct browser globals or imperative third-party APIs.dev@fineract.apache.org first —
or it is a bug fix, refactor or test change, where that does not apply.