Upload TRANSLATION.md with huggingface_hub
Browse files- TRANSLATION.md +606 -0
TRANSLATION.md
ADDED
|
@@ -0,0 +1,606 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🌍 Translation Guide for BentoPDF
|
| 2 |
+
|
| 3 |
+
This guide will help you add new languages or improve existing translations for BentoPDF.
|
| 4 |
+
|
| 5 |
+
## Table of Contents
|
| 6 |
+
|
| 7 |
+
- [Overview](#overview)
|
| 8 |
+
- [Quick Start](#quick-start)
|
| 9 |
+
- [Adding a New Language](#adding-a-new-language)
|
| 10 |
+
- [Translation File Structure](#translation-file-structure)
|
| 11 |
+
- [Where Translations Are Used](#where-translations-are-used)
|
| 12 |
+
- [Testing Your Translations](#testing-your-translations)
|
| 13 |
+
- [Translation Guidelines](#translation-guidelines)
|
| 14 |
+
- [Common Issues](#common-issues)
|
| 15 |
+
|
| 16 |
+
---
|
| 17 |
+
|
| 18 |
+
## Overview
|
| 19 |
+
|
| 20 |
+
BentoPDF uses **i18next** for internationalization (i18n). Currently supported languages:
|
| 21 |
+
|
| 22 |
+
- **English** (`en`) - Default
|
| 23 |
+
- **Belarusian** (`be`)
|
| 24 |
+
- **German** (`de`)
|
| 25 |
+
- **Spanish** (`es`)
|
| 26 |
+
- **French** (`fr`)
|
| 27 |
+
- **Italian** (`it`)
|
| 28 |
+
- **Portuguese** (`pt`)
|
| 29 |
+
- **Turkish** (`tr`)
|
| 30 |
+
- **Vietnamese** (`vi`)
|
| 31 |
+
- **Indonesian** (`id`)
|
| 32 |
+
- **Chinese** (`zh`)
|
| 33 |
+
- **Traditional Chinese (Taiwan)** (`zh-TW`)
|
| 34 |
+
|
| 35 |
+
The app automatically detects the language from the URL path:
|
| 36 |
+
|
| 37 |
+
- `/` or `/en/` → English (default)
|
| 38 |
+
- `/de/` → German
|
| 39 |
+
- `/fr/` → French
|
| 40 |
+
- etc.
|
| 41 |
+
|
| 42 |
+
### Architecture
|
| 43 |
+
|
| 44 |
+
BentoPDF uses a **static pre-rendering** approach for SEO-optimized i18n:
|
| 45 |
+
|
| 46 |
+
1. **Build time**: `scripts/generate-i18n-pages.mjs` generates localized HTML files in `dist/{lang}/`
|
| 47 |
+
2. **Dev/Preview**: `languageRouterPlugin` in `vite.config.ts` handles URL rewriting
|
| 48 |
+
3. **Production**: Nginx serves static files directly from language directories
|
| 49 |
+
|
| 50 |
+
---
|
| 51 |
+
|
| 52 |
+
## Quick Start
|
| 53 |
+
|
| 54 |
+
**To improve existing translations:**
|
| 55 |
+
|
| 56 |
+
1. Navigate to `public/locales/{language}/common.json` and `public/locales/{language}/tools.json`
|
| 57 |
+
2. Find the key you want to update
|
| 58 |
+
3. Change the translation value
|
| 59 |
+
4. Save and test
|
| 60 |
+
|
| 61 |
+
**To add a new language (e.g., Japanese `ja`):**
|
| 62 |
+
|
| 63 |
+
1. Copy `public/locales/en/` to `public/locales/ja/`
|
| 64 |
+
2. Translate all values in both `ja/common.json` and `ja/tools.json`
|
| 65 |
+
3. Add Japanese to `supportedLanguages` and `languageNames` in `src/js/i18n/i18n.ts`
|
| 66 |
+
4. Add `'ja'` to `SUPPORTED_LANGUAGES` in `vite.config.ts`
|
| 67 |
+
5. Restart the dev server
|
| 68 |
+
6. Run `npm run build` to generate static language pages
|
| 69 |
+
7. Test thoroughly
|
| 70 |
+
|
| 71 |
+
---
|
| 72 |
+
|
| 73 |
+
## Adding a New Language
|
| 74 |
+
|
| 75 |
+
Let's add **Spanish** as an example:
|
| 76 |
+
|
| 77 |
+
### Step 1: Create Translation Files
|
| 78 |
+
|
| 79 |
+
```bash
|
| 80 |
+
# Create the directory
|
| 81 |
+
mkdir -p public/locales/es
|
| 82 |
+
|
| 83 |
+
# Copy the English template
|
| 84 |
+
cp public/locales/en/common.json public/locales/es/common.json
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
### Step 2: Translate the JSON Files
|
| 88 |
+
|
| 89 |
+
Open `public/locales/es/common.json` and translate all the values:
|
| 90 |
+
|
| 91 |
+
```json
|
| 92 |
+
{
|
| 93 |
+
"nav": {
|
| 94 |
+
"home": "Inicio",
|
| 95 |
+
"about": "Acerca de",
|
| 96 |
+
"contact": "Contacto",
|
| 97 |
+
"allTools": "Todas las herramientas"
|
| 98 |
+
},
|
| 99 |
+
"hero": {
|
| 100 |
+
"title": "Tu conjunto de herramientas PDF gratuito y seguro",
|
| 101 |
+
"subtitle": "Combina, divide, comprime y edita archivos PDF directamente en tu navegador."
|
| 102 |
+
}
|
| 103 |
+
// ... continue translating all keys
|
| 104 |
+
}
|
| 105 |
+
```
|
| 106 |
+
|
| 107 |
+
⚠️ **Important**: Only translate the **values**, NOT the keys!
|
| 108 |
+
|
| 109 |
+
✅ **Correct:**
|
| 110 |
+
|
| 111 |
+
```json
|
| 112 |
+
"home": "Inicio"
|
| 113 |
+
```
|
| 114 |
+
|
| 115 |
+
❌ **Wrong:**
|
| 116 |
+
|
| 117 |
+
```json
|
| 118 |
+
"inicio": "Inicio"
|
| 119 |
+
```
|
| 120 |
+
|
| 121 |
+
Then do the same for `public/locales/fr/tools.json` to translate all tool names and descriptions.
|
| 122 |
+
|
| 123 |
+
### Step 3: Register the Language
|
| 124 |
+
|
| 125 |
+
Edit `src/js/i18n/i18n.ts`:
|
| 126 |
+
|
| 127 |
+
```typescript
|
| 128 |
+
// Add 'fr' to supported languages
|
| 129 |
+
export const supportedLanguages = ['en', 'de', 'es', 'fr', 'zh', 'vi'] as const;
|
| 130 |
+
export type SupportedLanguage = (typeof supportedLanguages)[number];
|
| 131 |
+
|
| 132 |
+
// Add French display name
|
| 133 |
+
export const languageNames: Record<SupportedLanguage, string> = {
|
| 134 |
+
en: 'English',
|
| 135 |
+
de: 'Deutsch',
|
| 136 |
+
fr: 'Français', // ← Add this
|
| 137 |
+
};
|
| 138 |
+
```
|
| 139 |
+
|
| 140 |
+
### Step 4: Update Vite Configuration
|
| 141 |
+
|
| 142 |
+
In `vite.config.ts`, add your language to the `SUPPORTED_LANGUAGES` array:
|
| 143 |
+
|
| 144 |
+
```typescript
|
| 145 |
+
const SUPPORTED_LANGUAGES = [
|
| 146 |
+
'en',
|
| 147 |
+
'de',
|
| 148 |
+
'es',
|
| 149 |
+
'zh',
|
| 150 |
+
'zh-TW',
|
| 151 |
+
'vi',
|
| 152 |
+
'it',
|
| 153 |
+
'id',
|
| 154 |
+
'tr',
|
| 155 |
+
'fr',
|
| 156 |
+
'pt',
|
| 157 |
+
'ja',
|
| 158 |
+
] as const;
|
| 159 |
+
```
|
| 160 |
+
|
| 161 |
+
> **Important**: This is required for both dev server routing and the build-time i18n generation.
|
| 162 |
+
|
| 163 |
+
### Step 5: Test Your Translation
|
| 164 |
+
|
| 165 |
+
```bash
|
| 166 |
+
# Restart the dev server
|
| 167 |
+
npm run dev
|
| 168 |
+
|
| 169 |
+
# Visit the Japanese version
|
| 170 |
+
# http://localhost:5173/ja/
|
| 171 |
+
```
|
| 172 |
+
|
| 173 |
+
### Step 6: Build and Verify Static Files
|
| 174 |
+
|
| 175 |
+
```bash
|
| 176 |
+
# Run build (includes i18n page generation)
|
| 177 |
+
npm run build
|
| 178 |
+
|
| 179 |
+
# Verify files were created
|
| 180 |
+
ls dist/ja/
|
| 181 |
+
# Should show: index.html, merge-pdf.html, etc.
|
| 182 |
+
```
|
| 183 |
+
|
| 184 |
+
---
|
| 185 |
+
|
| 186 |
+
## Translation File Structure
|
| 187 |
+
|
| 188 |
+
The `common.json` file is organized into logical sections:
|
| 189 |
+
|
| 190 |
+
```json
|
| 191 |
+
{
|
| 192 |
+
"nav": {
|
| 193 |
+
// Navigation menu items
|
| 194 |
+
},
|
| 195 |
+
"hero": {
|
| 196 |
+
// Homepage hero section
|
| 197 |
+
},
|
| 198 |
+
"features": {
|
| 199 |
+
// Features section
|
| 200 |
+
},
|
| 201 |
+
"tools": {
|
| 202 |
+
// Tool names and descriptions
|
| 203 |
+
},
|
| 204 |
+
"upload": {
|
| 205 |
+
// File upload UI
|
| 206 |
+
},
|
| 207 |
+
"settings": {
|
| 208 |
+
// Settings modal and keyboard shortcuts
|
| 209 |
+
},
|
| 210 |
+
"faq": {
|
| 211 |
+
// FAQ section
|
| 212 |
+
},
|
| 213 |
+
"footer": {
|
| 214 |
+
// Footer links and text
|
| 215 |
+
},
|
| 216 |
+
"compliance": {
|
| 217 |
+
// Security compliance information
|
| 218 |
+
},
|
| 219 |
+
"testimonials": {
|
| 220 |
+
// User testimonials
|
| 221 |
+
},
|
| 222 |
+
"support": {
|
| 223 |
+
// Support section
|
| 224 |
+
},
|
| 225 |
+
"alert": {
|
| 226 |
+
// Alert and error messages
|
| 227 |
+
}
|
| 228 |
+
}
|
| 229 |
+
```
|
| 230 |
+
|
| 231 |
+
### Key Naming Convention
|
| 232 |
+
|
| 233 |
+
- Use **camelCase** for keys: `"deletePage"` not `"delete_page"`
|
| 234 |
+
- Use **nested objects** for organization: `"nav.home"` is represented as:
|
| 235 |
+
```json
|
| 236 |
+
{
|
| 237 |
+
"nav": {
|
| 238 |
+
"home": "Home"
|
| 239 |
+
}
|
| 240 |
+
}
|
| 241 |
+
```
|
| 242 |
+
- Be descriptive: `"shortcutsWarning"` is better than `"warning1"`
|
| 243 |
+
|
| 244 |
+
---
|
| 245 |
+
|
| 246 |
+
## Where Translations Are Used
|
| 247 |
+
|
| 248 |
+
### 1. HTML Templates (`data-i18n` attribute)
|
| 249 |
+
|
| 250 |
+
```html
|
| 251 |
+
<!-- Translation key: nav.home -->
|
| 252 |
+
<a href="/" data-i18n="nav.home">Home</a>
|
| 253 |
+
```
|
| 254 |
+
|
| 255 |
+
The `data-i18n` attribute tells i18next which translation to use.
|
| 256 |
+
|
| 257 |
+
### 2. Tool Definitions
|
| 258 |
+
|
| 259 |
+
Tool names and descriptions are defined in `src/js/config/tools.ts` and use a special namespace:
|
| 260 |
+
|
| 261 |
+
```typescript
|
| 262 |
+
{
|
| 263 |
+
name: 'Merge PDF', // Used for shortcuts only
|
| 264 |
+
subtitle: 'Combine multiple PDFs into one file.',
|
| 265 |
+
}
|
| 266 |
+
```
|
| 267 |
+
|
| 268 |
+
In translations:
|
| 269 |
+
|
| 270 |
+
```json
|
| 271 |
+
{
|
| 272 |
+
"tools": {
|
| 273 |
+
"mergePdf": {
|
| 274 |
+
"name": "PDF zusammenführen",
|
| 275 |
+
"subtitle": "Mehrere PDFs in eine Datei kombinieren."
|
| 276 |
+
}
|
| 277 |
+
}
|
| 278 |
+
}
|
| 279 |
+
```
|
| 280 |
+
|
| 281 |
+
### 3. Dynamic JavaScript (`t()` function)
|
| 282 |
+
|
| 283 |
+
For translations that need to be applied dynamically:
|
| 284 |
+
|
| 285 |
+
```typescript
|
| 286 |
+
import { t } from './i18n/i18n';
|
| 287 |
+
|
| 288 |
+
const message = t('alert.error');
|
| 289 |
+
console.log(message); // "Error" or "Fehler" depending on language
|
| 290 |
+
```
|
| 291 |
+
|
| 292 |
+
### 4. Placeholders
|
| 293 |
+
|
| 294 |
+
For input placeholders:
|
| 295 |
+
|
| 296 |
+
```html
|
| 297 |
+
<input
|
| 298 |
+
type="text"
|
| 299 |
+
placeholder="Search for a tool..."
|
| 300 |
+
data-i18n-placeholder="tools.searchPlaceholder"
|
| 301 |
+
/>
|
| 302 |
+
```
|
| 303 |
+
|
| 304 |
+
In `common.json`:
|
| 305 |
+
|
| 306 |
+
```json
|
| 307 |
+
{
|
| 308 |
+
"tools": {
|
| 309 |
+
"searchPlaceholder": "Nach einem Tool suchen..."
|
| 310 |
+
}
|
| 311 |
+
}
|
| 312 |
+
```
|
| 313 |
+
|
| 314 |
+
---
|
| 315 |
+
|
| 316 |
+
## Testing Your Translations
|
| 317 |
+
|
| 318 |
+
### Manual Testing
|
| 319 |
+
|
| 320 |
+
1. **Start development server:**
|
| 321 |
+
|
| 322 |
+
```bash
|
| 323 |
+
npm run dev
|
| 324 |
+
```
|
| 325 |
+
|
| 326 |
+
2. **Visit each language:**
|
| 327 |
+
- English: `http://localhost:5173/en/`
|
| 328 |
+
- German: `http://localhost:5173/de/`
|
| 329 |
+
- Vietnamese: `http://localhost:5173/vi/`
|
| 330 |
+
- Indonesian: `http://localhost:5173/id/`
|
| 331 |
+
- Chinese: `http://localhost:5173/zh/`
|
| 332 |
+
- Traditional Chinese (Taiwan): `http://localhost:5173/zh-TW/`
|
| 333 |
+
- French: `http://localhost:5173/fr/`
|
| 334 |
+
- Your new language: `http://localhost:5173/es/`
|
| 335 |
+
|
| 336 |
+
3. **Check these pages:**
|
| 337 |
+
- Homepage (`/`)
|
| 338 |
+
- About page (`/about.html`)
|
| 339 |
+
- Contact page (`/contact.html`)
|
| 340 |
+
- FAQ page (`/faq.html`)
|
| 341 |
+
- Tool pages (e.g., `/merge-pdf.html`)
|
| 342 |
+
|
| 343 |
+
4. **Test these interactions:**
|
| 344 |
+
- Click the language switcher in the footer
|
| 345 |
+
- Navigate between pages
|
| 346 |
+
- Open the settings modal (click gear icon next to search)
|
| 347 |
+
- Try a tool to see upload messages
|
| 348 |
+
|
| 349 |
+
### Automated Checks
|
| 350 |
+
|
| 351 |
+
Check for missing translations:
|
| 352 |
+
|
| 353 |
+
```bash
|
| 354 |
+
# This will show any missing keys
|
| 355 |
+
node scripts/check-translations.js
|
| 356 |
+
```
|
| 357 |
+
|
| 358 |
+
_(If this script doesn't exist, you may need to create it or manually compare JSON files)_
|
| 359 |
+
|
| 360 |
+
### Browser Testing
|
| 361 |
+
|
| 362 |
+
Test in different browsers:
|
| 363 |
+
|
| 364 |
+
- Chrome/Edge
|
| 365 |
+
- Firefox
|
| 366 |
+
- Safari
|
| 367 |
+
|
| 368 |
+
---
|
| 369 |
+
|
| 370 |
+
## Translation Guidelines
|
| 371 |
+
|
| 372 |
+
### 1. Keep the Tone Consistent
|
| 373 |
+
|
| 374 |
+
BentoPDF is **friendly, clear, and professional**. Match this tone in your translations.
|
| 375 |
+
|
| 376 |
+
✅ **Good:**
|
| 377 |
+
|
| 378 |
+
```json
|
| 379 |
+
"hero.title": "Ihr kostenloses und sicheres PDF-Toolkit"
|
| 380 |
+
```
|
| 381 |
+
|
| 382 |
+
❌ **Too formal:**
|
| 383 |
+
|
| 384 |
+
```json
|
| 385 |
+
"hero.title": "Ihr gebührenfreies und gesichertes Werkzeug für PDF-Dokumente"
|
| 386 |
+
```
|
| 387 |
+
|
| 388 |
+
### 2. Preserve Formatting
|
| 389 |
+
|
| 390 |
+
Some strings contain HTML or special characters:
|
| 391 |
+
|
| 392 |
+
```json
|
| 393 |
+
{
|
| 394 |
+
"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."
|
| 395 |
+
}
|
| 396 |
+
```
|
| 397 |
+
|
| 398 |
+
When translating, **keep the HTML tags intact**:
|
| 399 |
+
|
| 400 |
+
```json
|
| 401 |
+
{
|
| 402 |
+
"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."
|
| 403 |
+
}
|
| 404 |
+
```
|
| 405 |
+
|
| 406 |
+
### 3. Handle Plurals and Gender
|
| 407 |
+
|
| 408 |
+
If your language has complex plural rules or gender distinctions, consult the [i18next pluralization guide](https://www.i18next.com/translation-function/plurals).
|
| 409 |
+
|
| 410 |
+
Example:
|
| 411 |
+
|
| 412 |
+
```json
|
| 413 |
+
{
|
| 414 |
+
"pages": "page",
|
| 415 |
+
"pages_plural": "pages"
|
| 416 |
+
}
|
| 417 |
+
```
|
| 418 |
+
|
| 419 |
+
### 4. Don't Translate Brand Names or Legal Terms
|
| 420 |
+
|
| 421 |
+
Keep these as-is:
|
| 422 |
+
|
| 423 |
+
- BentoPDF
|
| 424 |
+
- PDF
|
| 425 |
+
- GitHub
|
| 426 |
+
- Discord
|
| 427 |
+
- Chrome, Firefox, Safari, etc.
|
| 428 |
+
- Terms and Conditions
|
| 429 |
+
- Privacy Policy
|
| 430 |
+
- Licensing
|
| 431 |
+
|
| 432 |
+
### 5. Technical Terms
|
| 433 |
+
|
| 434 |
+
For technical terms, use commonly accepted translations in your language:
|
| 435 |
+
|
| 436 |
+
- "Merge" → "Fusionner" (French), "Zusammenführen" (German)
|
| 437 |
+
- "Split" → "Diviser" (French), "Teilen" (German)
|
| 438 |
+
- "Compress" → "Compresser" (French), "Komprimieren" (German)
|
| 439 |
+
|
| 440 |
+
If unsure, check how other PDF tools translate these terms in your language.
|
| 441 |
+
|
| 442 |
+
### 6. String Length
|
| 443 |
+
|
| 444 |
+
Some UI elements have limited space. Try to keep translations **similar in length** to the English version.
|
| 445 |
+
|
| 446 |
+
If a translation is much longer, test it visually to ensure it doesn't break the layout.
|
| 447 |
+
|
| 448 |
+
---
|
| 449 |
+
|
| 450 |
+
## Common Issues
|
| 451 |
+
|
| 452 |
+
### Issue: Translations Not Showing Up
|
| 453 |
+
|
| 454 |
+
**Solution:**
|
| 455 |
+
|
| 456 |
+
1. Clear your browser cache
|
| 457 |
+
2. Hard refresh (Ctrl+F5 or Cmd+Shift+R)
|
| 458 |
+
3. Check browser console for errors
|
| 459 |
+
4. Verify the JSON file is valid (no syntax errors)
|
| 460 |
+
|
| 461 |
+
### Issue: Some Text Still in English
|
| 462 |
+
|
| 463 |
+
**Possible causes:**
|
| 464 |
+
|
| 465 |
+
1. Missing translation key in your language file
|
| 466 |
+
2. Missing `data-i18n` attribute in HTML
|
| 467 |
+
3. Hardcoded text in JavaScript
|
| 468 |
+
|
| 469 |
+
**Solution:**
|
| 470 |
+
|
| 471 |
+
- Compare your language file with `en/common.json` to find missing keys
|
| 472 |
+
- Search the codebase for hardcoded strings
|
| 473 |
+
|
| 474 |
+
### Issue: JSON Syntax Error
|
| 475 |
+
|
| 476 |
+
**Symptoms:**
|
| 477 |
+
|
| 478 |
+
```
|
| 479 |
+
SyntaxError: Unexpected token } in JSON at position 1234
|
| 480 |
+
```
|
| 481 |
+
|
| 482 |
+
**Solution:**
|
| 483 |
+
|
| 484 |
+
- Use a JSON validator: https://jsonlint.com/
|
| 485 |
+
- Common mistakes:
|
| 486 |
+
- Trailing comma after last item
|
| 487 |
+
- Missing or extra quotes
|
| 488 |
+
- Unescaped quotes inside strings (use `\"`)
|
| 489 |
+
|
| 490 |
+
### Issue: Language Switcher Not Showing New Language
|
| 491 |
+
|
| 492 |
+
**Solution:**
|
| 493 |
+
Make sure you added the language to both arrays in `i18n.ts`:
|
| 494 |
+
|
| 495 |
+
```typescript
|
| 496 |
+
export const supportedLanguages = ['en', 'de', 'es', 'fr', 'zh', 'vi']; // ← Add here
|
| 497 |
+
export const languageNames = {
|
| 498 |
+
en: 'English',
|
| 499 |
+
de: 'Deutsch',
|
| 500 |
+
es: 'Español',
|
| 501 |
+
fr: 'Français', // ← And here
|
| 502 |
+
zh: '中文',
|
| 503 |
+
vi: 'Tiếng Việt',
|
| 504 |
+
};
|
| 505 |
+
```
|
| 506 |
+
|
| 507 |
+
### Issue: 404 Error When Accessing Language Pages
|
| 508 |
+
|
| 509 |
+
**Symptoms:**
|
| 510 |
+
Visiting `http://localhost:5173/ja/about.html` shows a 404 error page.
|
| 511 |
+
|
| 512 |
+
**Solution:**
|
| 513 |
+
You need to add your language code to `SUPPORTED_LANGUAGES` in `vite.config.ts`:
|
| 514 |
+
|
| 515 |
+
```typescript
|
| 516 |
+
const SUPPORTED_LANGUAGES = [
|
| 517 |
+
'en',
|
| 518 |
+
'de',
|
| 519 |
+
'es',
|
| 520 |
+
'zh',
|
| 521 |
+
'zh-TW',
|
| 522 |
+
'vi',
|
| 523 |
+
'it',
|
| 524 |
+
'id',
|
| 525 |
+
'tr',
|
| 526 |
+
'fr',
|
| 527 |
+
'pt',
|
| 528 |
+
'ja',
|
| 529 |
+
] as const;
|
| 530 |
+
```
|
| 531 |
+
|
| 532 |
+
After updating, restart the dev server:
|
| 533 |
+
|
| 534 |
+
```bash
|
| 535 |
+
npm run dev
|
| 536 |
+
```
|
| 537 |
+
|
| 538 |
+
---
|
| 539 |
+
|
| 540 |
+
## File Checklist
|
| 541 |
+
|
| 542 |
+
When adding a new language, make sure these files are updated:
|
| 543 |
+
|
| 544 |
+
- [ ] `public/locales/{lang}/common.json` - Main translation file
|
| 545 |
+
- [ ] `public/locales/{lang}/tools.json` - Tools translation file
|
| 546 |
+
- [ ] `src/js/i18n/i18n.ts` - Add to `supportedLanguages` and `languageNames`
|
| 547 |
+
- [ ] `vite.config.ts` - Add to `SUPPORTED_LANGUAGES` array
|
| 548 |
+
- [ ] Test all pages: homepage, about, contact, FAQ, tool pages
|
| 549 |
+
- [ ] Test settings modal and shortcuts
|
| 550 |
+
- [ ] Test language switcher in footer
|
| 551 |
+
- [ ] Verify URL routing works (`/{lang}/`)
|
| 552 |
+
- [ ] Run `npm run build` and verify `dist/{lang}/` folder is created
|
| 553 |
+
- [ ] Test that all tools load correctly
|
| 554 |
+
|
| 555 |
+
---
|
| 556 |
+
|
| 557 |
+
## Getting Help
|
| 558 |
+
|
| 559 |
+
If you have questions or need help:
|
| 560 |
+
|
| 561 |
+
1. Check existing translations in `public/locales/de/common.json` for reference
|
| 562 |
+
2. Open an issue on [GitHub](https://github.com/alam00000/bentopdf/issues)
|
| 563 |
+
3. Join our [Discord server](https://discord.gg/Bgq3Ay3f2w)
|
| 564 |
+
|
| 565 |
+
---
|
| 566 |
+
|
| 567 |
+
## Contributing Your Translation
|
| 568 |
+
|
| 569 |
+
Once you've completed a translation:
|
| 570 |
+
|
| 571 |
+
1. **Test thoroughly** (see [Testing Your Translations](#testing-your-translations))
|
| 572 |
+
2. **Fork the repository** on GitHub
|
| 573 |
+
3. **Create a new branch**: `git checkout -b add-french-translation`
|
| 574 |
+
4. **Commit your changes**: `git commit -m "Add French translation"`
|
| 575 |
+
5. **Push to your fork**: `git push origin add-french-translation`
|
| 576 |
+
6. **Open a Pull Request** with:
|
| 577 |
+
- Description of the language added
|
| 578 |
+
- Screenshots showing the translation in action
|
| 579 |
+
- Confirmation that you've tested all pages
|
| 580 |
+
|
| 581 |
+
Thank you for contributing to BentoPDF! 🎉
|
| 582 |
+
|
| 583 |
+
---
|
| 584 |
+
|
| 585 |
+
## Translation Progress
|
| 586 |
+
|
| 587 |
+
Current translation coverage:
|
| 588 |
+
|
| 589 |
+
| Language | Code | Status | Maintainer |
|
| 590 |
+
| ------------------- | ------- | -------------- | ---------- |
|
| 591 |
+
| English | `en` | ✅ Complete | Core team |
|
| 592 |
+
| German | `de` | ✅ Complete | Community |
|
| 593 |
+
| Spanish | `es` | ✅ Complete | Community |
|
| 594 |
+
| French | `fr` | ✅ Complete | Community |
|
| 595 |
+
| Italian | `it` | ✅ Complete | Community |
|
| 596 |
+
| Portuguese | `pt` | ✅ Complete | Community |
|
| 597 |
+
| Turkish | `tr` | ✅ Complete | Community |
|
| 598 |
+
| Vietnamese | `vi` | ✅ Complete | Community |
|
| 599 |
+
| Indonesian | `id` | ✅ Complete | Community |
|
| 600 |
+
| Chinese | `zh` | ✅ Complete | Community |
|
| 601 |
+
| Traditional Chinese | `zh-TW` | ✅ Complete | Community |
|
| 602 |
+
| Your Language | `??` | 🚧 In Progress | You? |
|
| 603 |
+
|
| 604 |
+
---
|
| 605 |
+
|
| 606 |
+
**Last Updated**: January 2026
|