This guide will help you add new languages or improve existing translations for BentoPDF.
- Overview
- Quick Start
- Adding a New Language
- Translation File Structure
- Where Translations Are Used
- Testing Your Translations
- Translation Guidelines
- Common Issues
BentoPDF uses i18next for internationalization (i18n). Currently supported languages:
- English (
en) - Default - German (
de) - Vietnamese (
vi)
The app automatically detects the language from the URL path:
/en/→ English/de/→ German/vi/→ Vietnamese
To improve existing translations:
- Navigate to
public/locales/{language}/common.json - Find the key you want to update
- Change the translation value
- Save and test
To add a new language (e.g., Spanish):
- Copy
public/locales/en/common.jsontopublic/locales/es/common.json - Translate all values in
es/common.json - Add Spanish to
supportedLanguagesinsrc/js/i18n/i18n.ts - Add Spanish name to
languageNamesinsrc/js/i18n/i18n.ts - Test thoroughly
Let's add French as an example:
# Create the directory
mkdir -p public/locales/fr
# Copy the English template
cp public/locales/en/common.json public/locales/fr/common.jsonOpen public/locales/fr/common.json and translate all the values:
{
"nav": {
"home": "Accueil",
"about": "À propos",
"contact": "Contact",
"allTools": "Tous les outils"
},
"hero": {
"title": "Votre boîte à outils PDF gratuite et sécurisée",
"subtitle": "Fusionnez, divisez, compressez et modifiez des PDF directement dans votre navigateur."
}
// ... continue translating all keys
}✅ Correct:
"home": "Accueil"❌ Wrong:
"accueil": "Accueil"Edit src/js/i18n/i18n.ts:
// Add 'fr' to supported languages
export const supportedLanguages = ['en', 'de', 'fr'] as const;
export type SupportedLanguage = (typeof supportedLanguages)[number];
// Add French display name
export const languageNames: Record<SupportedLanguage, string> = {
en: 'English',
de: 'Deutsch',
fr: 'Français', // ← Add this
};# Start the dev server
npm run dev
# Visit the French version
# http://localhost:5173/fr/The common.json file is organized into logical sections:
{
"nav": {
// Navigation menu items
},
"hero": {
// Homepage hero section
},
"features": {
// Features section
},
"tools": {
// Tool names and descriptions
},
"upload": {
// File upload UI
},
"settings": {
// Settings modal and keyboard shortcuts
},
"faq": {
// FAQ section
},
"footer": {
// Footer links and text
},
"compliance": {
// Security compliance information
},
"testimonials": {
// User testimonials
},
"support": {
// Support section
},
"alert": {
// Alert and error messages
}
}- Use camelCase for keys:
"deletePage"not"delete_page" - Use nested objects for organization:
"nav.home"is represented as:{ "nav": { "home": "Home" } } - Be descriptive:
"shortcutsWarning"is better than"warning1"
<!-- Translation key: nav.home -->
<a href="/" data-i18n="nav.home">Home</a>The data-i18n attribute tells i18next which translation to use.
Tool names and descriptions are defined in src/js/config/tools.ts and use a special namespace:
{
name: 'Merge PDF', // Used for shortcuts only
subtitle: 'Combine multiple PDFs into one file.',
}In translations:
{
"tools": {
"mergePdf": {
"name": "PDF zusammenführen",
"subtitle": "Mehrere PDFs in eine Datei kombinieren."
}
}
}For translations that need to be applied dynamically:
import { t } from './i18n/i18n';
const message = t('alert.error');
console.log(message); // "Error" or "Fehler" depending on languageFor input placeholders:
<input
type="text"
placeholder="Search for a tool..."
data-i18n-placeholder="tools.searchPlaceholder"
/>In common.json:
{
"tools": {
"searchPlaceholder": "Nach einem Tool suchen..."
}
}-
Start development server:
npm run dev
-
Visit each language:
- English:
http://localhost:5173/en/ - German:
http://localhost:5173/de/ - Vietnamese:
http://localhost:5173/vi/ - Your new language:
http://localhost:5173/fr/
- English:
-
Check these pages:
- Homepage (
/) - About page (
/about.html) - Contact page (
/contact.html) - FAQ page (
/faq.html) - Tool pages (e.g.,
/merge-pdf.html)
- Homepage (
-
Test these interactions:
- Click the language switcher in the footer
- Navigate between pages
- Open the settings modal (click gear icon next to search)
- Try a tool to see upload messages
Check for missing translations:
# This will show any missing keys
node scripts/check-translations.js(If this script doesn't exist, you may need to create it or manually compare JSON files)
Test in different browsers:
- Chrome/Edge
- Firefox
- Safari
BentoPDF is friendly, clear, and professional. Match this tone in your translations.
✅ Good:
"hero.title": "Ihr kostenloses und sicheres PDF-Toolkit"❌ Too formal:
"hero.title": "Ihr gebührenfreies und gesichertes Werkzeug für PDF-Dokumente"Some strings contain HTML or special characters:
{
"faq.analytics.answer": "We care about your privacy. BentoPDF does not track personal information. We use <a href=\"https://simpleanalytics.com\" class=\"text-indigo-400 hover:underline\" target=\"_blank\" rel=\"noopener noreferrer\">Simple Analytics</a> solely to see anonymous visit counts."
}When translating, keep the HTML tags intact:
{
"faq.analytics.answer": "Wir schätzen Ihre Privatsphäre. BentoPDF verfolgt keine persönlichen Informationen. Wir verwenden <a href=\"https://simpleanalytics.com\" class=\"text-indigo-400 hover:underline\" target=\"_blank\" rel=\"noopener noreferrer\">Simple Analytics</a> ausschließlich, um anonyme Besucherzahlen zu sehen."
}If your language has complex plural rules or gender distinctions, consult the i18next pluralization guide.
Example:
{
"pages": "page",
"pages_plural": "pages"
}Keep these as-is:
- BentoPDF
- GitHub
- Discord
- Chrome, Firefox, Safari, etc.
- Terms and Conditions
- Privacy Policy
- Licensing
For technical terms, use commonly accepted translations in your language:
- "Merge" → "Fusionner" (French), "Zusammenführen" (German)
- "Split" → "Diviser" (French), "Teilen" (German)
- "Compress" → "Compresser" (French), "Komprimieren" (German)
If unsure, check how other PDF tools translate these terms in your language.
Some UI elements have limited space. Try to keep translations similar in length to the English version.
If a translation is much longer, test it visually to ensure it doesn't break the layout.
Solution:
- Clear your browser cache
- Hard refresh (Ctrl+F5 or Cmd+Shift+R)
- Check browser console for errors
- Verify the JSON file is valid (no syntax errors)
Possible causes:
- Missing translation key in your language file
- Missing
data-i18nattribute in HTML - Hardcoded text in JavaScript
Solution:
- Compare your language file with
en/common.jsonto find missing keys - Search the codebase for hardcoded strings
Symptoms:
SyntaxError: Unexpected token } in JSON at position 1234
Solution:
- Use a JSON validator: https://jsonlint.com/
- Common mistakes:
- Trailing comma after last item
- Missing or extra quotes
- Unescaped quotes inside strings (use
\")
Solution:
Make sure you added the language to both arrays in i18n.ts:
export const supportedLanguages = ['en', 'de', 'fr']; // ← Add here
export const languageNames = {
en: 'English',
de: 'Deutsch',
fr: 'Français', // ← And here
};When adding a new language, make sure these files are updated:
-
public/locales/{lang}/common.json- Main translation file -
src/js/i18n/i18n.ts- Add tosupportedLanguagesandlanguageNames - Test all pages: homepage, about, contact, FAQ, tool pages
- Test settings modal and shortcuts
- Test language switcher in footer
- Verify URL routing works (
/{lang}/)
If you have questions or need help:
- Check existing translations in
public/locales/de/common.jsonfor reference - Open an issue on GitHub
- Join our Discord server
Once you've completed a translation:
- Test thoroughly (see Testing Your Translations)
- Fork the repository on GitHub
- Create a new branch:
git checkout -b add-french-translation - Commit your changes:
git commit -m "Add French translation" - Push to your fork:
git push origin add-french-translation - Open a Pull Request with:
- Description of the language added
- Screenshots showing the translation in action
- Confirmation that you've tested all pages
Thank you for contributing to BentoPDF! 🎉
Current translation coverage:
| Language | Code | Status | Maintainer |
|---|---|---|---|
| English | en |
✅ Complete | Core team |
| German | de |
🚧 In Progress | Core team |
| Vietnamese | vi |
✅ Complete | Community |
| Your Language | ?? |
🚧 In Progress | You? |
Last Updated: December 2025