Thanks for your interest in NutriTrace.
- Open an issue at github.com/traceapps/nutritrace/issues.
- Include your version (Settings → About), what you expected, and what you saw.
- For sync issues, include whether you're on PWA or native Android, and your server version.
- Don't paste server logs publicly without redacting —
LOG_LEVEL=debugincludes personal health data (HRV, RHR, sleep) and any tokens that happened to be in flight.
- Open an issue describing the use case before writing code — it helps avoid building something that won't get merged.
- Check FUTURE.md first; the feature may already be planned or intentionally deferred.
- Target the
devbranch, notmain. All work lands ondevfirst, gets tested there, and is bundled intomainat release time. PRs opened againstmainwill be asked to retarget. - Keep changes focused — one concern per PR.
- Match the existing code style (Svelte 4, no TypeScript).
- For server changes, ensure all SQL is parameterized and every new route has appropriate
requireAuth/requireAdminmiddleware. - Update
CHANGELOG.mdunder the unreleased section if your change is user-visible. - The Android shell lives in
android/; if you change web assets the maintainer will runnpx cap sync androidand rebuild the APK. - No DCO or CLA required.
NutriTrace uses svelte-i18n with one JSON file per locale in src/i18n/. The English file at src/i18n/en.json is the source of truth.
The easiest way to contribute translations is via Weblate — a browser-based translation platform that syncs directly with this repo. Pick a language, translate strings inline, and commits land as PRs automatically. No git, no JSON syntax, no code. You can also request a new language from within Weblate; the maintainer will register it in src/i18n/index.js and add it to the Settings language picker after the first batch of strings comes in.
If you'd rather bootstrap a language locally and open a PR directly:
- Copy
src/i18n/en.jsontosrc/i18n/<code>.jsonwhere<code>is the BCP-47 short code (fr,de,nl,es,pt,ja, etc.). - Translate the values. Leave the keys exactly as they are. Keep
{placeholder}tokens and any HTML tags (<strong>,<code>,<br>) intact and in the right grammatical position for your language. - In
src/i18n/index.js, register the new locale and add it toAVAILABLE_LOCALES:The label is what shows in the Settings → Regional & Units → Language picker. Use the language's native name (e.g.register('fr', () => import('./fr.json')); // ... export const AVAILABLE_LOCALES = [ { code: 'en', label: 'English' }, { code: 'fr', label: 'Français' }, ];
FrançaisnotFrench). - Run
npm run i18n:checkto confirm no keys are missing or orphaned. - Open a PR.
If new keys land in en.json between releases, your locale file will report them as "missing" in npm run i18n:check. The app will fall back to English for those strings until you translate them. There is no urgency — translate at your own pace.
The English source text may also change occasionally without renaming the key. We do not have automatic stale-translation detection, so a quick diff of en.json against the version you originally translated from is the most reliable way to catch these.
- Domain conventions matter. For nutrition labels, use the regulatory terms used on food packaging in your country (e.g. French food labels say
Glucides/Lipides/Protéines, not the literal translations of the English words). - Match the tone. NutriTrace's English copy is informal and direct ("How did today feel?"). Try to keep that register rather than translating to a more formal style.
- Length awareness. Some buttons are tight on small screens. If your translation is significantly longer than the English, test on a phone-sized viewport.
- Do not translate proper nouns or product names —
NutriTrace,OFF,USDA,Mealie,Trace(the AI assistant),Open Food Factsstay as-is.
Every user-facing string added to the app should be extracted into en.json and rendered through svelte-i18n's $_() helper. Hardcoded English literals in templates are the reason translation coverage lags the codebase — please prevent them at PR time rather than retrofit them later.
The pattern:
<script>
import { _ } from 'svelte-i18n';
</script>
<h1>{$_('routes.diary.title')}</h1>
<input placeholder={$_('routes.foods.search_placeholder')} />Then in src/i18n/en.json:
"routes": {
"diary": {
"title": "Diary"
},
"foods": {
"search_placeholder": "Search foods…"
}
}Guidelines:
- Group by area, not by page.
settings.notifications.sectionis better thansettings_notifications_section. - Only add English in your PR. Do not machine-translate or hand-translate into other languages you don't natively speak — that misrepresents contributor work. The
en.jsonaddition is enough; translators fill in their locale files in follow-up PRs.svelte-i18n'sfallbackLocale: 'en'renders English until a translation lands. - Skip developer-facing strings — error stacks, log messages, JSON payload keys, class names. Only pull out what a user reads on screen.
- Interpolation uses
{$_('key', { values: { name: user.name } })}and{name}in the JSON value. Prefer this over string concatenation so translators can reorder words. - Run
npm run i18n:checkbefore opening the PR. It flags orphaned keys and missing translations across every locale file, catching typos and stale entries.
If you're adding a section that has a lot of copy, group all the new keys under one namespace in en.json so they can be reviewed together.
Do not add translations for locales you don't natively speak, and do not merge machine-translated content into a contributor's locale file. If a section can't be translated at code-write time (nobody on the PR speaks the language), extract to en.json, open a follow-up "Translations wanted" issue linking the new keys, and let a native speaker fill them in. svelte-i18n's English fallback keeps the app fully functional in the meantime.
The full client-side string surface is extracted as of v1.1.0 — navigation, all Settings sections, Diary, Foods, Wellness, Goals, Statistics, the wizard, auth flow, the AI assistant, action sheets, toasts, dialog copy. Any new user-facing string added to the app is expected to land as a key in en.json in the same commit (see instrumenting guidance above); hardcoded English literals get flagged in review. npm run i18n:check runs against every locale file to catch missing translations and orphaned keys.
Server-side strings (email subject lines, push notification bodies, AI system prompts) are not currently translatable and stay English.
README screenshots live in docs/screenshots/ (numbered prefix for sort order). If your PR meaningfully changes the UI shown in any of them, please replace the affected screenshot at the same dimensions and theme (dark) so the README stays accurate.
By contributing you agree that your contribution is licensed under AGPL-3.0, the same license as the rest of the server and PWA code.