Initial commit
This commit is contained in:
258
references/translations/PLATFORM_TRANSLATIONS.md
Normal file
258
references/translations/PLATFORM_TRANSLATIONS.md
Normal file
@@ -0,0 +1,258 @@
|
||||
# Platform Translation System
|
||||
|
||||
## Overview
|
||||
|
||||
This system allows dynamic customization of platform-specific terminology (like "Timebank.cc", "Timebanker", etc.) across all languages without hardcoding values in translation files.
|
||||
|
||||
## Configuration
|
||||
|
||||
Platform translations are defined in `config/timebank-cc.php` under the `platform_translations` array:
|
||||
|
||||
```php
|
||||
'platform_translations' => [
|
||||
'en' => [
|
||||
'platform_name' => ' Timebank.cc',
|
||||
'platform_name_legal' => 'association Timebank.cc',
|
||||
'platform_name_short' => 'Timebank',
|
||||
'platform_slogan' => 'Your time is currency',
|
||||
'platform_user' => 'Timebanker',
|
||||
'platform_users' => 'Timebankers',
|
||||
'platform_principles' => 'Timebank principles',
|
||||
],
|
||||
'nl' => [
|
||||
// Dutch translations...
|
||||
],
|
||||
// ... other languages
|
||||
],
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
Located in `app/Helpers/PlatformConfig.php`:
|
||||
|
||||
### Core Function
|
||||
|
||||
- **`platform_trans($key, $locale = null, $default = null)`**
|
||||
- Get any platform translation by key
|
||||
- Falls back to base language (English) if not found in current locale
|
||||
- Example: `platform_trans('platform_users')` → "Timebankers"
|
||||
|
||||
### Convenience Functions
|
||||
|
||||
- **`platform_name($locale = null)`** → " Timebank.cc"
|
||||
- **`platform_name_short($locale = null)`** → "Timebank"
|
||||
- **`platform_name_legal($locale = null)`** → "association Timebank.cc"
|
||||
- **`platform_slogan($locale = null)`** → "Your time is currency"
|
||||
- **`platform_user($locale = null)`** → "Timebanker" (singular)
|
||||
- **`platform_users($locale = null)`** → "Timebankers" (plural)
|
||||
- **`platform_principles($locale = null)`** → "Timebank principles"
|
||||
- **`platform_currency_name($locale = null)`** → "Hour" (singular)
|
||||
- **`platform_currency_name_plural($locale = null)`** → "Hours" (plural)
|
||||
- **`platform_currency_symbol($locale = null)`** → "H"
|
||||
|
||||
### Automatic Placeholder Replacement
|
||||
|
||||
- **`trans_with_platform($key, $replace = [], $locale = null)`**
|
||||
- Translates a string and replaces platform placeholders
|
||||
- Supports all standard Laravel `__()` parameters
|
||||
|
||||
## Translation File Placeholders
|
||||
|
||||
JSON translation files use placeholders that get replaced at runtime:
|
||||
|
||||
| Placeholder | Replaced With | Example |
|
||||
|------------|---------------|---------|
|
||||
| `@PLATFORM_NAME@` | platform_name() | " Timebank.cc" |
|
||||
| `@PLATFORM_NAME_SHORT@` | platform_name_short() | "Timebank" |
|
||||
| `@PLATFORM_NAME_LEGAL@` | platform_name_legal() | "association Timebank.cc" |
|
||||
| `@PLATFORM_SLOGAN@` | platform_slogan() | "Your time is currency" |
|
||||
| `@PLATFORM_USER@` | platform_user() | "Timebanker" |
|
||||
| `@PLATFORM_USERS@` | platform_users() | "Timebankers" |
|
||||
| `@PLATFORM_PRINCIPLES@` | platform_principles() | "Timebank principles" |
|
||||
| `@PLATFORM_CURRENCY_NAME@` | platform_currency_name() | "Hour" |
|
||||
| `@PLATFORM_CURRENCY_NAME_PLURAL@` | platform_currency_name_plural() | "Hours" |
|
||||
| `@PLATFORM_CURRENCY_SYMBOL@` | platform_currency_symbol() | "H" |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### In Blade Templates
|
||||
|
||||
```blade
|
||||
{{-- Direct helper usage --}}
|
||||
<h1>Welcome to {{ platform_name() }}!</h1>
|
||||
<p>Join {{ platform_users() }} worldwide</p>
|
||||
|
||||
{{-- With translation placeholders --}}
|
||||
<p>{{ trans_with_platform('Activities or skills you offer to other @PLATFORM_USERS@') }}</p>
|
||||
|
||||
{{-- Force specific locale --}}
|
||||
<p>{{ platform_name('de') }}</p> {{-- German version --}}
|
||||
```
|
||||
|
||||
### In PHP Controllers/Classes
|
||||
|
||||
```php
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
// In email subject
|
||||
$subject = 'Welcome to ' . platform_name() . '!';
|
||||
|
||||
// With translations
|
||||
$message = trans_with_platform('Your profile on @PLATFORM_NAME@ just received a star');
|
||||
|
||||
// Get translation for specific locale
|
||||
$germanSlogan = platform_slogan('de'); // "Deine Zeit ist Währung"
|
||||
```
|
||||
|
||||
### In Livewire Components
|
||||
|
||||
```php
|
||||
public function mount()
|
||||
{
|
||||
$this->pageTitle = platform_users();
|
||||
$this->welcomeMessage = trans_with_platform('Welcome new @PLATFORM_USER@!');
|
||||
}
|
||||
```
|
||||
|
||||
## Language Support
|
||||
|
||||
Platform translations are available in:
|
||||
|
||||
- **English (en)** - Base language
|
||||
- **Dutch (nl)** - Nederlandse vertalingen
|
||||
- **German (de)** - Deutsche Übersetzungen
|
||||
- **Spanish (es)** - Traducciones al español
|
||||
- **French (fr)** - Traductions françaises
|
||||
|
||||
## Customization Guide
|
||||
|
||||
### Changing Platform Terminology
|
||||
|
||||
1. Edit `config/timebank-cc.php`
|
||||
2. Update the `platform_translations` array for each language
|
||||
3. Clear config cache: `php artisan config:clear`
|
||||
|
||||
Example - Rebranding to "TimeCurrency":
|
||||
|
||||
```php
|
||||
'platform_translations' => [
|
||||
'en' => [
|
||||
'platform_name' => 'TimeCurrency',
|
||||
'platform_name_short' => 'TimeCurrency',
|
||||
'platform_user' => 'TimeCurrency member',
|
||||
'platform_users' => 'TimeCurrency members',
|
||||
// ... update other keys
|
||||
],
|
||||
// ... repeat for all languages
|
||||
],
|
||||
```
|
||||
|
||||
### Adding New Languages
|
||||
|
||||
1. Add new locale to `platform_translations` in config
|
||||
2. Provide translations for all keys
|
||||
3. Create corresponding JSON translation file in `resources/lang/`
|
||||
|
||||
## Migration to Database
|
||||
|
||||
When ready to move configuration to database:
|
||||
|
||||
1. Create migration for `platform_translations` table
|
||||
2. Seed table with current config values
|
||||
3. Update `platform_trans()` function in `app/Helpers/PlatformConfig.php`:
|
||||
|
||||
```php
|
||||
function platform_trans($key, $locale = null, $default = null)
|
||||
{
|
||||
$locale = $locale ?? app()->getLocale();
|
||||
|
||||
// Query database instead of config
|
||||
$translation = DB::table('platform_translations')
|
||||
->where('key', $key)
|
||||
->where('locale', $locale)
|
||||
->value('value');
|
||||
|
||||
// Fallback logic remains the same
|
||||
if ($translation === null && $locale !== $baseLanguage) {
|
||||
$translation = DB::table('platform_translations')
|
||||
->where('key', $key)
|
||||
->where('locale', $baseLanguage)
|
||||
->value('value');
|
||||
}
|
||||
|
||||
return $translation ?? $default ?? $key;
|
||||
}
|
||||
```
|
||||
|
||||
All existing code will continue working without modification!
|
||||
|
||||
## Testing
|
||||
|
||||
Test helpers across all locales:
|
||||
|
||||
```bash
|
||||
php artisan tinker
|
||||
```
|
||||
|
||||
```php
|
||||
// Test English
|
||||
app()->setLocale('en');
|
||||
echo platform_users(); // "Timebankers"
|
||||
|
||||
// Test Dutch
|
||||
app()->setLocale('nl');
|
||||
echo platform_users(); // "Timebankers"
|
||||
echo platform_principles(); // "Timebank principes"
|
||||
|
||||
// Test German
|
||||
app()->setLocale('de');
|
||||
echo platform_users(); // "Zeitbankers"
|
||||
echo platform_slogan(); // "Deine Zeit ist Währung"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Placeholders not being replaced
|
||||
|
||||
- Ensure you're using `trans_with_platform()` instead of `__()`
|
||||
- Check that placeholder syntax is correct: `@PLATFORM_NAME@` (not `{PLATFORM_NAME}`)
|
||||
|
||||
### Wrong language returned
|
||||
|
||||
- Check current locale: `app()->getLocale()`
|
||||
- Verify translation exists in config for that locale
|
||||
- Fallback to English if translation missing
|
||||
|
||||
### Autoload issues
|
||||
|
||||
```bash
|
||||
composer dump-autoload
|
||||
php artisan config:clear
|
||||
php artisan cache:clear
|
||||
```
|
||||
|
||||
## File Reference
|
||||
|
||||
- **Helper**: `app/Helpers/PlatformConfig.php`
|
||||
- **Config**: `config/timebank-cc.php` (platform_translations section)
|
||||
- **Translations**: `resources/lang/{locale}.json`
|
||||
- **Backups**: `resources/lang/{locale}.json.bak`
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always use helpers** in new code instead of hardcoding "Timebank.cc"
|
||||
2. **Use `trans_with_platform()`** when displaying translated strings with platform terms
|
||||
3. **Test all locales** when changing platform terminology
|
||||
4. **Document custom terms** in comments when creating new translation keys
|
||||
5. **Keep backups** of JSON files before major changes
|
||||
|
||||
## Examples in Codebase
|
||||
|
||||
See these files for reference implementations:
|
||||
- Translation files: `resources/lang/nl.json` (lines with @PLATFORM placeholders)
|
||||
- Test examples: Run `php artisan tinker` and use code from Testing section above
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-10-23
|
||||
**Version**: 1.0
|
||||
110
references/translations/PLATFORM_TRANSLATIONS_QUICK_REFERENCE.md
Normal file
110
references/translations/PLATFORM_TRANSLATIONS_QUICK_REFERENCE.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# Platform Translations - Quick Reference
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
```php
|
||||
// In Blade templates
|
||||
{{ platform_name() }} // Timebank.cc
|
||||
{{ platform_users() }} // Timebankers (or Zeitbankers in German)
|
||||
{{ platform_slogan() }} // Your time is currency
|
||||
|
||||
// With automatic placeholder replacement
|
||||
{{ trans_with_platform('Welcome @PLATFORM_USERS@!') }}
|
||||
```
|
||||
|
||||
## 📋 Available Functions
|
||||
|
||||
| Function | Returns (EN) | Returns (DE) |
|
||||
|----------|-------------|--------------|
|
||||
| `platform_name()` | " Timebank.cc" | " Timebank.cc" |
|
||||
| `platform_name_short()` | "Timebank" | "Timebank" |
|
||||
| `platform_user()` | "Timebanker" | "Zeitbanker" |
|
||||
| `platform_users()` | "Timebankers" | "Zeitbankers" |
|
||||
| `platform_slogan()` | "Your time is currency" | "Deine Zeit ist Währung" |
|
||||
| `platform_principles()` | "Timebank principles" | "Timebank Prinzipien" |
|
||||
| `platform_currency_name()` | "Hour" | "Stunde" |
|
||||
| `platform_currency_name_plural()` | "Hours" | "Stunden" |
|
||||
| `platform_currency_symbol()` | "H" | "Std" |
|
||||
|
||||
## 🔧 Common Use Cases
|
||||
|
||||
### Email Subject Lines
|
||||
```php
|
||||
$subject = 'Welcome to ' . platform_name();
|
||||
```
|
||||
|
||||
### Page Titles
|
||||
```blade
|
||||
<h1>{{ platform_users() }} Directory</h1>
|
||||
```
|
||||
|
||||
### Translated Content with Platform Terms
|
||||
```blade
|
||||
{{ trans_with_platform('Join @PLATFORM_USERS@ worldwide') }}
|
||||
```
|
||||
|
||||
### Multi-language Support
|
||||
```php
|
||||
// Get German version regardless of current locale
|
||||
$germanSlogan = platform_slogan('de');
|
||||
```
|
||||
|
||||
## 📝 Translation File Placeholders
|
||||
|
||||
When editing JSON translation files, use these placeholders:
|
||||
|
||||
```json
|
||||
{
|
||||
"Welcome new user!": "Welkom nieuwe @PLATFORM_USER@!",
|
||||
"Connect with others": "Maak verbinding met andere @PLATFORM_USERS@",
|
||||
"About us": "Over @PLATFORM_NAME_SHORT@"
|
||||
}
|
||||
```
|
||||
|
||||
**Available Placeholders:**
|
||||
- `@PLATFORM_NAME@` → " Timebank.cc"
|
||||
- `@PLATFORM_NAME_SHORT@` → "Timebank"
|
||||
- `@PLATFORM_USER@` → "Timebanker"
|
||||
- `@PLATFORM_USERS@` → "Timebankers"
|
||||
- `@PLATFORM_PRINCIPLES@` → "Timebank principles"
|
||||
- `@PLATFORM_SLOGAN@` → "Your time is currency"
|
||||
- `@PLATFORM_NAME_LEGAL@` → "association Timebank.cc"
|
||||
- `@PLATFORM_CURRENCY_NAME@` → "Hour"
|
||||
- `@PLATFORM_CURRENCY_NAME_PLURAL@` → "Hours"
|
||||
- `@PLATFORM_CURRENCY_SYMBOL@` → "H"
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
Edit `config/timebank-cc.php`:
|
||||
|
||||
```php
|
||||
'platform_translations' => [
|
||||
'en' => [
|
||||
'platform_name' => ' Timebank.cc',
|
||||
'platform_users' => 'Timebankers',
|
||||
// ... other keys
|
||||
],
|
||||
],
|
||||
```
|
||||
|
||||
After changes: `php artisan config:clear`
|
||||
|
||||
## ✅ Testing
|
||||
|
||||
```bash
|
||||
php artisan tinker
|
||||
```
|
||||
|
||||
```php
|
||||
app()->setLocale('nl');
|
||||
echo platform_users(); // Test Dutch
|
||||
```
|
||||
|
||||
## 📚 Full Documentation
|
||||
|
||||
See `PLATFORM_TRANSLATIONS.md` for complete documentation.
|
||||
|
||||
---
|
||||
|
||||
**System Status**: ✅ Active (89 placeholders per language)
|
||||
**Languages**: EN, NL, DE, ES, FR
|
||||
484
references/translations/TRANSLATION_UPDATE_GUIDE.md
Normal file
484
references/translations/TRANSLATION_UPDATE_GUIDE.md
Normal file
@@ -0,0 +1,484 @@
|
||||
# Translation Update Guide
|
||||
|
||||
This guide explains how to update all translation files when new content with translation strings is added to views.
|
||||
|
||||
## Overview
|
||||
|
||||
The application uses Laravel's translation system with TWO types of translation files:
|
||||
|
||||
1. **JSON Translation Files** (for simple strings) - managed by `kargnas/laravel-ai-translator` package
|
||||
2. **PHP Translation Files** (for dynamic strings with parameters) - managed manually
|
||||
|
||||
**JSON Translation Files Location:** `resources/lang/`
|
||||
- `en.json` - English (source language)
|
||||
- `nl.json` - Dutch
|
||||
- `de.json` - German
|
||||
- `es.json` - Spanish
|
||||
- `fr.json` - French
|
||||
|
||||
**PHP Translation Files Location:** `resources/lang/[locale]/`
|
||||
- `messages.php` - Dynamic messages with placeholders (e.g., `:name`, `:count`)
|
||||
- `routes.php` - Route-related translations
|
||||
- `validation.php` - Validation messages (Laravel default)
|
||||
- `passwords.php` - Password reset messages (Laravel default)
|
||||
|
||||
**IMPORTANT:** PHP translation files should NEVER be converted to JSON format. Laravel automatically uses PHP files when the key is not found in JSON files. Keep them separate to avoid conflicts.
|
||||
|
||||
## When to Update Translations
|
||||
|
||||
Update translations whenever you:
|
||||
- Add new `__('Translation string')` calls in Blade views
|
||||
- Add new `__('Translation string')` calls in PHP code
|
||||
- Add new `trans('Translation string')` calls
|
||||
- Modify existing translation strings (creates new keys)
|
||||
|
||||
## Step-by-Step Update Process
|
||||
|
||||
### Step 1: Add English Translation Strings
|
||||
|
||||
First, add your new translation strings to `resources/lang/en.json`:
|
||||
|
||||
```bash
|
||||
# Open the English translation file
|
||||
nano resources/lang/en.json
|
||||
```
|
||||
|
||||
Add your new keys in alphabetical order:
|
||||
|
||||
```json
|
||||
{
|
||||
"existing.key": "Existing value",
|
||||
"new.key.one": "New translation string one",
|
||||
"new.key.two": "New translation string two",
|
||||
"another.key": "Another value"
|
||||
}
|
||||
```
|
||||
|
||||
**Important Rules for Translation Keys:**
|
||||
- Use descriptive, meaningful key names
|
||||
- Use dot notation for organization (e.g., `messages.welcome`, `buttons.submit`)
|
||||
- Keep keys lowercase
|
||||
- Use underscores for spaces in multi-word keys
|
||||
- Avoid special characters except dots and underscores
|
||||
|
||||
### Step 2: Sync Translation Files
|
||||
|
||||
Sync all language files to ensure they have the same keys as `en.json`:
|
||||
|
||||
```bash
|
||||
php artisan ai-translator:sync-json
|
||||
```
|
||||
|
||||
This command:
|
||||
- Adds missing keys from `en.json` to all other language files
|
||||
- Removes keys that don't exist in `en.json` from other files
|
||||
- Keeps existing translations intact
|
||||
- Adds English values as placeholders for new keys
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
Syncing translation files...
|
||||
✓ nl.json: Added 15 keys, removed 0 keys
|
||||
✓ de.json: Added 15 keys, removed 0 keys
|
||||
✓ es.json: Added 15 keys, removed 0 keys
|
||||
✓ fr.json: Added 15 keys, removed 0 keys
|
||||
```
|
||||
|
||||
### Step 3: Translate New Keys with AI
|
||||
|
||||
Translate the new keys to all languages using the AI translator:
|
||||
|
||||
#### Option A: Translate All Languages Sequentially
|
||||
|
||||
Use the provided script to translate all languages in sequence:
|
||||
|
||||
```bash
|
||||
./translate-new-keys.sh
|
||||
```
|
||||
|
||||
#### Option B: Translate Each Language Individually
|
||||
|
||||
Translate Dutch:
|
||||
```bash
|
||||
php artisan ai-translator:translate-json --source=en --locale=nl --non-interactive --chunk=100
|
||||
```
|
||||
|
||||
Translate German:
|
||||
```bash
|
||||
php artisan ai-translator:translate-json --source=en --locale=de --non-interactive --chunk=100
|
||||
```
|
||||
|
||||
Translate Spanish:
|
||||
```bash
|
||||
php artisan ai-translator:translate-json --source=en --locale=es --non-interactive --chunk=100
|
||||
```
|
||||
|
||||
Translate French:
|
||||
```bash
|
||||
php artisan ai-translator:translate-json --source=en --locale=fr --non-interactive --chunk=100
|
||||
```
|
||||
|
||||
**Translation Parameters:**
|
||||
- `--source=en` - Source language (English)
|
||||
- `--locale=XX` - Target language code
|
||||
- `--non-interactive` - Don't ask for confirmation
|
||||
- `--chunk=100` - Process 100 keys at a time (reduces API load)
|
||||
|
||||
### Step 4: Verify Translation Results
|
||||
|
||||
Check that all files now have the same number of keys:
|
||||
|
||||
```bash
|
||||
php -r "
|
||||
\$en = json_decode(file_get_contents('resources/lang/en.json'), true);
|
||||
\$nl = json_decode(file_get_contents('resources/lang/nl.json'), true);
|
||||
\$de = json_decode(file_get_contents('resources/lang/de.json'), true);
|
||||
\$es = json_decode(file_get_contents('resources/lang/es.json'), true);
|
||||
\$fr = json_decode(file_get_contents('resources/lang/fr.json'), true);
|
||||
|
||||
echo \"Translation key counts:\n\";
|
||||
echo \" en: \" . count(\$en) . \" keys\n\";
|
||||
echo \" nl: \" . count(\$nl) . \" keys\n\";
|
||||
echo \" de: \" . count(\$de) . \" keys\n\";
|
||||
echo \" es: \" . count(\$es) . \" keys\n\";
|
||||
echo \" fr: \" . count(\$fr) . \" keys\n\";
|
||||
"
|
||||
```
|
||||
|
||||
**Expected Result:** All files should have the same number of keys.
|
||||
|
||||
### Step 5: Test Translations in Application
|
||||
|
||||
1. Clear Laravel cache:
|
||||
```bash
|
||||
php artisan config:clear
|
||||
php artisan cache:clear
|
||||
```
|
||||
|
||||
2. Test the new translations in your browser by switching languages
|
||||
3. Verify that all new strings appear correctly translated
|
||||
|
||||
## Quick Reference Scripts
|
||||
|
||||
### translate-new-keys.sh
|
||||
|
||||
Create this script in your project root for easy sequential translation:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
echo "=== TRANSLATING NEW KEYS TO ALL LANGUAGES ==="
|
||||
echo ""
|
||||
|
||||
# Sync first to ensure all files have the same keys
|
||||
echo "Step 1: Syncing translation files..."
|
||||
php artisan ai-translator:sync-json
|
||||
echo ""
|
||||
|
||||
# Translate each language
|
||||
echo "Step 2: Translating to Dutch (nl)..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=nl --non-interactive --chunk=100
|
||||
sleep 5
|
||||
|
||||
echo ""
|
||||
echo "Step 3: Translating to German (de)..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=de --non-interactive --chunk=100
|
||||
sleep 5
|
||||
|
||||
echo ""
|
||||
echo "Step 4: Translating to Spanish (es)..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=es --non-interactive --chunk=100
|
||||
sleep 5
|
||||
|
||||
echo ""
|
||||
echo "Step 5: Translating to French (fr)..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=fr --non-interactive --chunk=100
|
||||
|
||||
echo ""
|
||||
echo "=== TRANSLATION COMPLETE ==="
|
||||
echo ""
|
||||
|
||||
# Show final counts
|
||||
echo "Final key counts:"
|
||||
php -r "
|
||||
\$en = json_decode(file_get_contents('resources/lang/en.json'), true);
|
||||
\$nl = json_decode(file_get_contents('resources/lang/nl.json'), true);
|
||||
\$de = json_decode(file_get_contents('resources/lang/de.json'), true);
|
||||
\$es = json_decode(file_get_contents('resources/lang/es.json'), true);
|
||||
\$fr = json_decode(file_get_contents('resources/lang/fr.json'), true);
|
||||
|
||||
echo \" en: \" . count(\$en) . \" keys\n\";
|
||||
echo \" nl: \" . count(\$nl) . \" keys\n\";
|
||||
echo \" de: \" . count(\$de) . \" keys\n\";
|
||||
echo \" es: \" . count(\$es) . \" keys\n\";
|
||||
echo \" fr: \" . count(\$fr) . \" keys\n\";
|
||||
"
|
||||
```
|
||||
|
||||
Make it executable:
|
||||
```bash
|
||||
chmod +x translate-new-keys.sh
|
||||
```
|
||||
|
||||
## Translation Configuration
|
||||
|
||||
The AI translator is configured in `config/ai-translator.php`:
|
||||
|
||||
**Key Settings:**
|
||||
- **Provider:** Anthropic (Claude)
|
||||
- **Model:** claude-3-haiku-20240307
|
||||
- **API Key:** Set in `.env` as `ANTHROPIC_API_KEY`
|
||||
- **Source Locale:** en (English)
|
||||
- **Tone:** Friendly, intuitive, informal
|
||||
- **Addressing Style:** Informal (je/du/tú/tu, not u/Sie/usted/vous)
|
||||
|
||||
**Additional Rules Applied:**
|
||||
```php
|
||||
'additional_rules' => [
|
||||
'default' => [
|
||||
"Use a friendly, intuitive, and informal tone of voice. Simple vocabulary is preferred over advanced vocabulary.",
|
||||
"Use informal addressing: 'je' in Dutch, 'tu' in French, 'du' in German, 'tú' in Spanish (not formal 'u', 'vous', 'Sie', 'usted').",
|
||||
],
|
||||
],
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: AI Translator Skips All Keys
|
||||
|
||||
**Symptom:** Message says "All strings are already translated. Skipping."
|
||||
|
||||
**Cause:** The translator considers `key === value` as "already translated"
|
||||
|
||||
**Solution:** Remove untranslated keys before running the translator:
|
||||
|
||||
```bash
|
||||
php -r "
|
||||
\$file = 'resources/lang/nl.json';
|
||||
\$data = json_decode(file_get_contents(\$file), true);
|
||||
\$filtered = array_filter(\$data, function(\$value, \$key) {
|
||||
return \$value !== \$key; // Remove where key equals value
|
||||
}, ARRAY_FILTER_USE_BOTH);
|
||||
file_put_contents(\$file, json_encode(\$filtered, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL);
|
||||
"
|
||||
```
|
||||
|
||||
### Issue: Translation Files Out of Sync
|
||||
|
||||
**Symptom:** Different number of keys in different language files
|
||||
|
||||
**Solution:** Run the sync command:
|
||||
```bash
|
||||
php artisan ai-translator:sync-json
|
||||
```
|
||||
|
||||
### Issue: API Rate Limiting
|
||||
|
||||
**Symptom:** Translation fails with rate limit errors
|
||||
|
||||
**Solution:**
|
||||
1. Reduce chunk size: `--chunk=50`
|
||||
2. Add delays between translations (see translate-new-keys.sh script)
|
||||
3. Translate one language at a time with longer delays
|
||||
|
||||
### Issue: Some Keys Not Translating
|
||||
|
||||
**Symptom:** Some keys remain in English in other language files
|
||||
|
||||
**Solution:**
|
||||
1. Check that the key exists in `en.json`
|
||||
2. Verify the key format (valid JSON)
|
||||
3. Check for special characters that might break translation
|
||||
4. Manually review and re-run translation for specific locale
|
||||
|
||||
### Issue: Translations Show Literal Keys Instead of Values
|
||||
|
||||
**Symptom:** Translations display literal key names like "messages.login_success" instead of the actual translated text
|
||||
|
||||
**Cause:** This occurs when PHP file-based translations (e.g., `resources/lang/en/messages.php`) conflict with JSON translations. The AI translator may have incorrectly added keys from PHP translation files to JSON files with literal key names as values.
|
||||
|
||||
**Example of the problem:**
|
||||
```json
|
||||
{
|
||||
"messages.login_success": "messages.login_success"
|
||||
}
|
||||
```
|
||||
|
||||
This prevents Laravel from falling back to the PHP translation file.
|
||||
|
||||
**Solution:** Remove all conflicting keys that start with `messages.` from all JSON translation files:
|
||||
|
||||
```bash
|
||||
php -r "
|
||||
\$messagesPhp = require('resources/lang/en/messages.php');
|
||||
\$languages = ['en', 'nl', 'de', 'es', 'fr'];
|
||||
|
||||
// Get list of conflicting keys
|
||||
\$conflictingKeys = [];
|
||||
foreach (\$messagesPhp as \$key => \$value) {
|
||||
\$conflictingKeys[] = 'messages.' . \$key;
|
||||
}
|
||||
|
||||
echo 'Removing ' . count(\$conflictingKeys) . ' conflicting keys from JSON files...' . PHP_EOL;
|
||||
|
||||
foreach (\$languages as \$lang) {
|
||||
\$file = 'resources/lang/' . \$lang . '.json';
|
||||
\$data = json_decode(file_get_contents(\$file), true);
|
||||
\$originalCount = count(\$data);
|
||||
|
||||
foreach (\$conflictingKeys as \$key) {
|
||||
if (isset(\$data[\$key])) {
|
||||
unset(\$data[\$key]);
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents(\$file, json_encode(\$data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL);
|
||||
|
||||
echo \$lang . '.json: ' . \$originalCount . ' -> ' . count(\$data) . ' keys' . PHP_EOL;
|
||||
}
|
||||
|
||||
echo 'Done! Clear cache with: php artisan config:clear && php artisan cache:clear' . PHP_EOL;
|
||||
"
|
||||
```
|
||||
|
||||
After removing the conflicting keys, clear Laravel's cache:
|
||||
```bash
|
||||
php artisan config:clear && php artisan cache:clear
|
||||
```
|
||||
|
||||
**Important Note:** This project uses BOTH JSON translation files (for simple strings) AND PHP translation files (for dynamic strings with parameters). The `resources/lang/en/messages.php` file (and its counterparts in other languages) should NEVER be duplicated into JSON files. Laravel will automatically use PHP files when the key is not found in JSON files.
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Always Start with English
|
||||
- Add all new translation strings to `en.json` first
|
||||
- Use clear, concise English that translates well
|
||||
- Avoid idioms or culturally-specific phrases
|
||||
|
||||
### 2. Use Consistent Key Naming
|
||||
```
|
||||
Good:
|
||||
messages.welcome_message
|
||||
buttons.submit_form
|
||||
errors.validation_failed
|
||||
|
||||
Bad:
|
||||
welcomeMsg
|
||||
btnSubmit
|
||||
error_1
|
||||
```
|
||||
|
||||
### 3. Organize Keys Logically
|
||||
```json
|
||||
{
|
||||
"auth.login": "Log in",
|
||||
"auth.logout": "Log out",
|
||||
"auth.register": "Register",
|
||||
|
||||
"profile.edit": "Edit profile",
|
||||
"profile.delete": "Delete profile",
|
||||
"profile.settings": "Profile settings",
|
||||
|
||||
"messages.welcome": "Welcome",
|
||||
"messages.goodbye": "Goodbye"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Test Before Committing
|
||||
- Always test translations in the application
|
||||
- Check all 5 languages
|
||||
- Verify formatting (capitalization, punctuation)
|
||||
- Ensure placeholders (`:name`, `:count`) work correctly
|
||||
|
||||
### 5. Regular Cleanup
|
||||
Periodically check for unused translation keys:
|
||||
|
||||
```bash
|
||||
php artisan ai-translator:find-unused --format=table
|
||||
```
|
||||
|
||||
Review and remove unused keys to keep files maintainable.
|
||||
|
||||
## Common Translation Patterns
|
||||
|
||||
### Simple Strings
|
||||
```php
|
||||
__('Welcome to our platform')
|
||||
```
|
||||
|
||||
### With Placeholders
|
||||
```php
|
||||
__('Hello, :name!', ['name' => $user->name])
|
||||
```
|
||||
|
||||
### Pluralization
|
||||
```php
|
||||
trans_choice('{0} No items|{1} One item|[2,*] :count items', $count)
|
||||
```
|
||||
|
||||
### Conditional Translation
|
||||
```php
|
||||
__('messages.' . ($type === 'success' ? 'success_message' : 'error_message'))
|
||||
```
|
||||
|
||||
## Git Workflow
|
||||
|
||||
When committing translation updates:
|
||||
|
||||
```bash
|
||||
# Stage all translation files
|
||||
git add resources/lang/*.json
|
||||
|
||||
# Commit with descriptive message
|
||||
git commit -m "Add translations for new mailing features
|
||||
|
||||
- Added 25 new translation keys for mailing management
|
||||
- Translated to nl, de, es, fr using AI translator
|
||||
- All files now have 1,414 keys"
|
||||
|
||||
# Push changes
|
||||
git push origin main
|
||||
```
|
||||
|
||||
## Related Files
|
||||
|
||||
- **Config:** `config/ai-translator.php`
|
||||
- **Translation Files:** `resources/lang/*.json`
|
||||
- **Helper Scripts:**
|
||||
- `retranslate-informal.sh` - Re-translate all languages with informal style
|
||||
- `translate-new-keys.sh` - Translate only new keys (create this)
|
||||
- `sync-translation-files.php` - Manual sync script (backup method)
|
||||
|
||||
## Quick Command Reference
|
||||
|
||||
```bash
|
||||
# Sync all translation files
|
||||
php artisan ai-translator:sync-json
|
||||
|
||||
# Translate to specific language
|
||||
php artisan ai-translator:translate-json --source=en --locale=nl --non-interactive --chunk=100
|
||||
|
||||
# Find unused translation keys
|
||||
php artisan ai-translator:find-unused
|
||||
|
||||
# Count keys in all files
|
||||
php -r "\$en = json_decode(file_get_contents('resources/lang/en.json'), true); echo count(\$en);"
|
||||
|
||||
# Clear Laravel cache
|
||||
php artisan config:clear && php artisan cache:clear
|
||||
```
|
||||
|
||||
## Environment Requirements
|
||||
|
||||
- **PHP:** 8.1+
|
||||
- **Laravel:** 9+
|
||||
- **Package:** kargnas/laravel-ai-translator
|
||||
- **API Key:** Anthropic Claude API (set in `.env` as `ANTHROPIC_API_KEY`)
|
||||
- **Internet:** Required for AI translation API calls
|
||||
|
||||
## Support
|
||||
|
||||
For issues with:
|
||||
- **Laravel AI Translator Package:** https://github.com/kargnas/laravel-ai-translator
|
||||
- **Translation Process:** Review this guide or check logs in `/tmp/`
|
||||
- **API Issues:** Check Anthropic API status and your API key validity
|
||||
48
references/translations/extract-unused-keys.php
Normal file
48
references/translations/extract-unused-keys.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Extract unused translation keys from en.json for review
|
||||
*/
|
||||
|
||||
// Run the find-unused command and capture output
|
||||
exec('php artisan ai-translator:find-unused --format=table --show-files --no-interaction 2>&1', $output);
|
||||
|
||||
$inEnJson = false;
|
||||
$unusedKeys = [];
|
||||
|
||||
foreach ($output as $line) {
|
||||
// Check if we're in the en.json section
|
||||
if (strpos($line, 'en.json:') !== false) {
|
||||
$inEnJson = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if we've moved to another file
|
||||
if ($inEnJson && preg_match('/^\s*[a-z\-]+\.php:/', $line)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Extract the key if we're in en.json section
|
||||
if ($inEnJson && preg_match('/│\s+([^\s│]+)\s+│/', $line, $matches)) {
|
||||
$unusedKeys[] = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
echo "=== UNUSED TRANSLATION KEYS IN en.json ===\n";
|
||||
echo "Total: " . count($unusedKeys) . " keys\n\n";
|
||||
|
||||
// Save to file
|
||||
file_put_contents('/tmp/unused-en-keys-list.txt', implode("\n", $unusedKeys));
|
||||
|
||||
// Show first 50 for preview
|
||||
echo "First 50 unused keys:\n";
|
||||
echo str_repeat('-', 80) . "\n";
|
||||
foreach (array_slice($unusedKeys, 0, 50) as $key) {
|
||||
echo " - " . $key . "\n";
|
||||
}
|
||||
|
||||
if (count($unusedKeys) > 50) {
|
||||
echo "\n... and " . (count($unusedKeys) - 50) . " more.\n";
|
||||
}
|
||||
|
||||
echo "\nFull list saved to: /tmp/unused-en-keys-list.txt\n";
|
||||
76
references/translations/format-unused-keys.php
Normal file
76
references/translations/format-unused-keys.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Format unused translation keys from the table output
|
||||
*/
|
||||
|
||||
$file = '/tmp/unused-keys-table.txt';
|
||||
$content = file_get_contents($file);
|
||||
$lines = explode("\n", $content);
|
||||
|
||||
$keys = [];
|
||||
$inTable = false;
|
||||
|
||||
foreach ($lines as $line) {
|
||||
// Skip header lines and decorations
|
||||
if (strpos($line, 'Translation Key') !== false) {
|
||||
$inTable = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$inTable) continue;
|
||||
|
||||
// Extract key and value from table format
|
||||
if (preg_match('/^\|\s+([a-z][^\|]+?)\s+\|\s+(.+?)\s+\|$/i', $line, $matches)) {
|
||||
$key = trim($matches[1]);
|
||||
$value = trim($matches[2]);
|
||||
|
||||
// Skip divider lines
|
||||
if (strpos($key, '---') !== false || strpos($key, '===') !== false) continue;
|
||||
|
||||
$keys[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
echo "=== UNUSED TRANSLATION KEYS ===\n";
|
||||
echo "Total found: " . count($keys) . " keys\n";
|
||||
echo str_repeat('=', 100) . "\n\n";
|
||||
|
||||
// Group by prefix
|
||||
$grouped = [];
|
||||
foreach ($keys as $key => $value) {
|
||||
$parts = explode('.', $key);
|
||||
$prefix = $parts[0];
|
||||
|
||||
if (!isset($grouped[$prefix])) {
|
||||
$grouped[$prefix] = [];
|
||||
}
|
||||
$grouped[$prefix][$key] = $value;
|
||||
}
|
||||
|
||||
ksort($grouped);
|
||||
|
||||
// Display each group
|
||||
foreach ($grouped as $prefix => $items) {
|
||||
echo "\n" . strtoupper($prefix) . " (" . count($items) . " keys)\n";
|
||||
echo str_repeat('-', 100) . "\n";
|
||||
|
||||
foreach ($items as $key => $value) {
|
||||
// Truncate very long values
|
||||
if (strlen($value) > 70) {
|
||||
$value = substr($value, 0, 67) . '...';
|
||||
}
|
||||
printf(" %-50s → %s\n", $key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n" . str_repeat('=', 100) . "\n";
|
||||
echo "TOTAL: " . count($keys) . " unused translation keys\n\n";
|
||||
|
||||
echo "NOTE: Some keys may be used dynamically (e.g., __(\$variable)).\n";
|
||||
echo "Review carefully before deleting.\n\n";
|
||||
|
||||
// Save clean list for user reference
|
||||
$cleanList = array_keys($keys);
|
||||
file_put_contents('/tmp/unused-keys-clean.txt', implode("\n", $cleanList));
|
||||
echo "Clean key list saved to: /tmp/unused-keys-clean.txt\n";
|
||||
79
references/translations/generate-review-csv.php
Normal file
79
references/translations/generate-review-csv.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Generate a CSV file for manual review of unused translation keys
|
||||
*/
|
||||
|
||||
$file = '/tmp/unused-keys-table.txt';
|
||||
$content = file_get_contents($file);
|
||||
$lines = explode("\n", $content);
|
||||
|
||||
$keys = [];
|
||||
$inTable = false;
|
||||
|
||||
foreach ($lines as $line) {
|
||||
// Skip header lines and decorations
|
||||
if (strpos($line, 'Translation Key') !== false) {
|
||||
$inTable = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$inTable) continue;
|
||||
|
||||
// Extract key and value from table format
|
||||
if (preg_match('/^\|\s+([a-z][^\|]+?)\s+\|\s+(.+?)\s+\|$/i', $line, $matches)) {
|
||||
$key = trim($matches[1]);
|
||||
$value = trim($matches[2]);
|
||||
|
||||
// Skip divider lines
|
||||
if (strpos($key, '---') !== false || strpos($key, '===') !== false) continue;
|
||||
|
||||
$keys[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
echo "Found " . count($keys) . " unused keys\n";
|
||||
echo "Generating CSV file...\n";
|
||||
|
||||
// Create CSV
|
||||
$csv = fopen('unused-keys-review.csv', 'w');
|
||||
|
||||
// Write header
|
||||
fputcsv($csv, ['Key', 'Value', 'Category', 'Action', 'Notes']);
|
||||
|
||||
// Write data rows
|
||||
foreach ($keys as $key => $value) {
|
||||
// Determine category from key prefix
|
||||
$parts = explode('.', $key);
|
||||
$category = count($parts) > 1 ? $parts[0] : 'other';
|
||||
|
||||
fputcsv($csv, [$key, $value, $category, '', '']);
|
||||
}
|
||||
|
||||
fclose($csv);
|
||||
|
||||
echo "CSV file created: unused-keys-review.csv\n";
|
||||
echo "Total keys: " . count($keys) . "\n\n";
|
||||
|
||||
// Show category breakdown
|
||||
$categories = [];
|
||||
foreach ($keys as $key => $value) {
|
||||
$parts = explode('.', $key);
|
||||
$category = count($parts) > 1 ? $parts[0] : 'other';
|
||||
|
||||
if (!isset($categories[$category])) {
|
||||
$categories[$category] = 0;
|
||||
}
|
||||
$categories[$category]++;
|
||||
}
|
||||
|
||||
arsort($categories);
|
||||
|
||||
echo "Breakdown by category:\n";
|
||||
echo str_repeat('-', 50) . "\n";
|
||||
foreach ($categories as $cat => $count) {
|
||||
printf(" %-30s %4d keys\n", $cat, $count);
|
||||
}
|
||||
|
||||
echo "\nYou can now open 'unused-keys-review.csv' in a spreadsheet application\n";
|
||||
echo "to manually review and mark which keys to keep or delete.\n";
|
||||
41
references/translations/prepare-for-ai-translator.php
Normal file
41
references/translations/prepare-for-ai-translator.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Prepare JSON files for Laravel AI Translator by removing untranslated keys
|
||||
* The AI translator only translates keys that don't exist in the target file
|
||||
*/
|
||||
|
||||
$locale = $argv[1] ?? null;
|
||||
|
||||
if (!$locale || !in_array($locale, ['nl', 'de', 'es', 'fr'])) {
|
||||
echo "Usage: php prepare-for-ai-translator.php <locale>\n";
|
||||
echo "Available locales: nl, de, es, fr\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$file = "resources/lang/{$locale}.json";
|
||||
$translations = json_decode(file_get_contents($file), true);
|
||||
|
||||
// Create backup
|
||||
copy($file, "{$file}.backup");
|
||||
|
||||
// Keep only translated keys (where value !== key)
|
||||
$translatedOnly = [];
|
||||
foreach ($translations as $key => $value) {
|
||||
if ($key !== $value) {
|
||||
$translatedOnly[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$before = count($translations);
|
||||
$after = count($translatedOnly);
|
||||
$removed = $before - $after;
|
||||
|
||||
ksort($translatedOnly);
|
||||
file_put_contents($file, json_encode($translatedOnly, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL);
|
||||
|
||||
echo "Prepared {$locale}.json for AI translator:\n";
|
||||
echo " - Backup saved to {$locale}.json.backup\n";
|
||||
echo " - Removed {$removed} untranslated keys\n";
|
||||
echo " - Kept {$after} translated keys\n";
|
||||
echo "\nNow run: php artisan ai-translator:translate-json --source=en --locale={$locale} --chunk=100\n";
|
||||
53
references/translations/replace-tag-with-label.php
Normal file
53
references/translations/replace-tag-with-label.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Replace 'tag'/'tags' with 'label'/'labels' in Dutch translation values
|
||||
* Only modifies the translation values (right side), not the keys (left side)
|
||||
*/
|
||||
|
||||
$file = "resources/lang/nl.json";
|
||||
$translations = json_decode(file_get_contents($file), true);
|
||||
|
||||
// Create backup
|
||||
copy($file, "{$file}.backup");
|
||||
|
||||
$replacements = 0;
|
||||
$modified = [];
|
||||
|
||||
foreach ($translations as $key => $value) {
|
||||
$original = $value;
|
||||
|
||||
// Replace all variations preserving case
|
||||
// Order matters: do plural before singular to avoid double replacements
|
||||
$value = str_replace('Tags', 'Labels', $value);
|
||||
$value = str_replace('tags', 'labels', $value);
|
||||
$value = str_replace('Tag', 'Label', $value);
|
||||
$value = str_replace('tag', 'label', $value);
|
||||
|
||||
if ($original !== $value) {
|
||||
$replacements++;
|
||||
$modified[$key] = [
|
||||
'before' => $original,
|
||||
'after' => $value
|
||||
];
|
||||
$translations[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Save updated translations
|
||||
file_put_contents($file, json_encode($translations, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL);
|
||||
|
||||
echo "Replaced 'tag'/'tags' with 'label'/'labels' in nl.json:\n";
|
||||
echo " - Backup saved to nl.json.backup\n";
|
||||
echo " - Made {$replacements} replacements in {$replacements} translation values\n\n";
|
||||
|
||||
if ($replacements > 0) {
|
||||
echo "Modified translations:\n";
|
||||
echo str_repeat('=', 80) . "\n";
|
||||
foreach ($modified as $key => $changes) {
|
||||
echo "Key: {$key}\n";
|
||||
echo " Before: {$changes['before']}\n";
|
||||
echo " After: {$changes['after']}\n";
|
||||
echo str_repeat('-', 80) . "\n";
|
||||
}
|
||||
}
|
||||
87
references/translations/retranslate-informal.sh
Executable file
87
references/translations/retranslate-informal.sh
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=== RE-TRANSLATING ALL LANGUAGES WITH INFORMAL STYLE ==="
|
||||
echo ""
|
||||
echo "This will backup and then re-translate all language files using informal addressing:"
|
||||
echo " - Dutch: 'je' (not 'u')"
|
||||
echo " - German: 'du' (not 'Sie')"
|
||||
echo " - French: 'tu' (not 'vous')"
|
||||
echo " - Spanish: 'tú' (not 'usted')"
|
||||
echo ""
|
||||
|
||||
# Backup current translations
|
||||
echo "Creating backups of current translations..."
|
||||
cp resources/lang/nl.json resources/lang/nl.json.formal-backup
|
||||
cp resources/lang/de.json resources/lang/de.json.formal-backup
|
||||
cp resources/lang/es.json resources/lang/es.json.formal-backup
|
||||
cp resources/lang/fr.json resources/lang/fr.json.formal-backup
|
||||
echo "Backups created with .formal-backup extension"
|
||||
echo ""
|
||||
|
||||
# Delete all translated files so AI translator sees them as "missing"
|
||||
echo "Removing current translations to trigger full re-translation..."
|
||||
rm resources/lang/nl.json
|
||||
rm resources/lang/de.json
|
||||
rm resources/lang/es.json
|
||||
rm resources/lang/fr.json
|
||||
|
||||
# Create empty files
|
||||
echo "{}" > resources/lang/nl.json
|
||||
echo "{}" > resources/lang/de.json
|
||||
echo "{}" > resources/lang/es.json
|
||||
echo "{}" > resources/lang/fr.json
|
||||
echo "Empty translation files created"
|
||||
echo ""
|
||||
|
||||
# Translate each language sequentially
|
||||
echo "Starting Dutch (nl) translation with informal style..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=nl --non-interactive --chunk=100 2>&1 | tee /tmp/retranslate-nl-informal.log
|
||||
echo ""
|
||||
echo "Dutch complete! Waiting 10 seconds before next language..."
|
||||
sleep 10
|
||||
|
||||
echo ""
|
||||
echo "Starting German (de) translation with informal style..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=de --non-interactive --chunk=100 2>&1 | tee /tmp/retranslate-de-informal.log
|
||||
echo ""
|
||||
echo "German complete! Waiting 10 seconds before next language..."
|
||||
sleep 10
|
||||
|
||||
echo ""
|
||||
echo "Starting Spanish (es) translation with informal style..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=es --non-interactive --chunk=100 2>&1 | tee /tmp/retranslate-es-informal.log
|
||||
echo ""
|
||||
echo "Spanish complete! Waiting 10 seconds before next language..."
|
||||
sleep 10
|
||||
|
||||
echo ""
|
||||
echo "Starting French (fr) translation with informal style..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=fr --non-interactive --chunk=100 2>&1 | tee /tmp/retranslate-fr-informal.log
|
||||
echo ""
|
||||
|
||||
echo ""
|
||||
echo "=== ALL TRANSLATIONS COMPLETE WITH INFORMAL STYLE ==="
|
||||
echo ""
|
||||
|
||||
# Show final counts
|
||||
echo "Final key counts:"
|
||||
php -r "
|
||||
\$en = json_decode(file_get_contents('resources/lang/en.json'), true);
|
||||
\$nl = json_decode(file_get_contents('resources/lang/nl.json'), true);
|
||||
\$de = json_decode(file_get_contents('resources/lang/de.json'), true);
|
||||
\$es = json_decode(file_get_contents('resources/lang/es.json'), true);
|
||||
\$fr = json_decode(file_get_contents('resources/lang/fr.json'), true);
|
||||
|
||||
echo \" en: \" . count(\$en) . \" keys\n\";
|
||||
echo \" nl: \" . count(\$nl) . \" keys\n\";
|
||||
echo \" de: \" . count(\$de) . \" keys\n\";
|
||||
echo \" es: \" . count(\$es) . \" keys\n\";
|
||||
echo \" fr: \" . count(\$fr) . \" keys\n\";
|
||||
"
|
||||
|
||||
echo ""
|
||||
echo "Formal backups are available at:"
|
||||
echo " - resources/lang/nl.json.formal-backup"
|
||||
echo " - resources/lang/de.json.formal-backup"
|
||||
echo " - resources/lang/es.json.formal-backup"
|
||||
echo " - resources/lang/fr.json.formal-backup"
|
||||
67
references/translations/show-unused-en-keys.php
Normal file
67
references/translations/show-unused-en-keys.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Display unused translation keys from en.json in a readable format
|
||||
*/
|
||||
|
||||
// Run the find-unused command and capture output
|
||||
exec('php artisan ai-translator:find-unused --format=table --show-files --no-interaction 2>&1', $output);
|
||||
|
||||
$inEnJson = false;
|
||||
$unusedKeys = [];
|
||||
|
||||
foreach ($output as $line) {
|
||||
// Check if we're in the en.json section
|
||||
if (strpos($line, 'en.json:') !== false) {
|
||||
$inEnJson = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if we've moved to another file
|
||||
if ($inEnJson && preg_match('/^[a-z\-]+\.php:/', $line)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Extract the key if we're in en.json section
|
||||
if ($inEnJson && preg_match('/│\s+([^\s│]+)\s+│\s+(.+?)\s+│/', $line, $matches)) {
|
||||
$key = trim($matches[1]);
|
||||
$value = trim($matches[2]);
|
||||
if ($key && $value) {
|
||||
$unusedKeys[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "=== UNUSED TRANSLATION KEYS IN en.json ===\n";
|
||||
echo "Total: " . count($unusedKeys) . " keys\n";
|
||||
echo str_repeat('=', 80) . "\n\n";
|
||||
|
||||
// Group keys by prefix for easier review
|
||||
$grouped = [];
|
||||
foreach ($unusedKeys as $key => $value) {
|
||||
$prefix = explode('.', $key)[0];
|
||||
if (!isset($grouped[$prefix])) {
|
||||
$grouped[$prefix] = [];
|
||||
}
|
||||
$grouped[$prefix][$key] = $value;
|
||||
}
|
||||
|
||||
// Sort groups alphabetically
|
||||
ksort($grouped);
|
||||
|
||||
// Display each group
|
||||
foreach ($grouped as $prefix => $keys) {
|
||||
echo "\n" . strtoupper($prefix) . " (" . count($keys) . " keys):\n";
|
||||
echo str_repeat('-', 80) . "\n";
|
||||
|
||||
foreach ($keys as $key => $value) {
|
||||
// Truncate long values for readability
|
||||
$displayValue = strlen($value) > 60 ? substr($value, 0, 57) . '...' : $value;
|
||||
echo sprintf(" %-40s → %s\n", $key, $displayValue);
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n\n" . str_repeat('=', 80) . "\n";
|
||||
echo "Total unused keys: " . count($unusedKeys) . "\n";
|
||||
echo "\nNOTE: Some keys may be used dynamically (e.g., __(\$variable))\n";
|
||||
echo "Please review carefully before deleting.\n";
|
||||
54
references/translations/sync-translation-files.php
Normal file
54
references/translations/sync-translation-files.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Sync all translation files to have the same keys as en.json
|
||||
* Missing keys will use the English text as placeholder
|
||||
*/
|
||||
|
||||
$locales = ['nl', 'de', 'es', 'fr'];
|
||||
$enFile = 'resources/lang/en.json';
|
||||
|
||||
// Load English source
|
||||
$en = json_decode(file_get_contents($enFile), true);
|
||||
echo "Source (en.json): " . count($en) . " keys\n\n";
|
||||
|
||||
foreach ($locales as $locale) {
|
||||
$file = "resources/lang/{$locale}.json";
|
||||
|
||||
// Load existing translations
|
||||
$translations = json_decode(file_get_contents($file), true);
|
||||
$before = count($translations);
|
||||
|
||||
// Create backup
|
||||
copy($file, "{$file}.backup");
|
||||
|
||||
// Merge: keep existing translations, add missing keys with English value
|
||||
$synced = [];
|
||||
foreach ($en as $key => $value) {
|
||||
if (isset($translations[$key])) {
|
||||
// Keep existing translation
|
||||
$synced[$key] = $translations[$key];
|
||||
} else {
|
||||
// Add missing key with English value as placeholder
|
||||
$synced[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort alphabetically by key
|
||||
ksort($synced);
|
||||
|
||||
// Save synced file
|
||||
file_put_contents($file, json_encode($synced, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL);
|
||||
|
||||
$after = count($synced);
|
||||
$added = $after - $before;
|
||||
|
||||
echo "{$locale}.json:\n";
|
||||
echo " Before: {$before} keys\n";
|
||||
echo " After: {$after} keys\n";
|
||||
echo " Added: {$added} keys (with English placeholders)\n";
|
||||
echo " Backup: {$file}.backup\n\n";
|
||||
}
|
||||
|
||||
echo "✓ All files synced to " . count($en) . " keys\n";
|
||||
echo "\nNext step: Run AI translation to translate the placeholder keys\n";
|
||||
49
references/translations/translate-all-sequential.sh
Executable file
49
references/translations/translate-all-sequential.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=== TRANSLATING ALL LANGUAGES SEQUENTIALLY ==="
|
||||
echo ""
|
||||
|
||||
echo "Starting Dutch (nl) translation..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=nl --non-interactive --chunk=100
|
||||
echo ""
|
||||
echo "Dutch complete! Waiting 10 seconds before next language..."
|
||||
sleep 10
|
||||
|
||||
echo ""
|
||||
echo "Starting German (de) translation..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=de --non-interactive --chunk=100
|
||||
echo ""
|
||||
echo "German complete! Waiting 10 seconds before next language..."
|
||||
sleep 10
|
||||
|
||||
echo ""
|
||||
echo "Starting Spanish (es) translation..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=es --non-interactive --chunk=100
|
||||
echo ""
|
||||
echo "Spanish complete! Waiting 10 seconds before next language..."
|
||||
sleep 10
|
||||
|
||||
echo ""
|
||||
echo "Starting French (fr) translation..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=fr --non-interactive --chunk=100
|
||||
echo ""
|
||||
|
||||
echo ""
|
||||
echo "=== ALL TRANSLATIONS COMPLETE ==="
|
||||
echo ""
|
||||
|
||||
# Show final counts
|
||||
echo "Final key counts:"
|
||||
php -r "
|
||||
\$en = json_decode(file_get_contents('resources/lang/en.json'), true);
|
||||
\$nl = json_decode(file_get_contents('resources/lang/nl.json'), true);
|
||||
\$de = json_decode(file_get_contents('resources/lang/de.json'), true);
|
||||
\$es = json_decode(file_get_contents('resources/lang/es.json'), true);
|
||||
\$fr = json_decode(file_get_contents('resources/lang/fr.json'), true);
|
||||
|
||||
echo \" en: \" . count(\$en) . \" keys\n\";
|
||||
echo \" nl: \" . count(\$nl) . \" keys\n\";
|
||||
echo \" de: \" . count(\$de) . \" keys\n\";
|
||||
echo \" es: \" . count(\$es) . \" keys\n\";
|
||||
echo \" fr: \" . count(\$fr) . \" keys\n\";
|
||||
"
|
||||
79
references/translations/translate-new-keys.sh
Executable file
79
references/translations/translate-new-keys.sh
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=== TRANSLATING NEW KEYS TO ALL LANGUAGES ==="
|
||||
echo ""
|
||||
|
||||
# Sync first to ensure all files have the same keys
|
||||
echo "Step 1: Syncing translation files..."
|
||||
php artisan ai-translator:sync-json
|
||||
echo ""
|
||||
|
||||
# Count keys before translation
|
||||
echo "Current key counts:"
|
||||
php -r "
|
||||
\$en = json_decode(file_get_contents('resources/lang/en.json'), true);
|
||||
\$nl = json_decode(file_get_contents('resources/lang/nl.json'), true);
|
||||
\$de = json_decode(file_get_contents('resources/lang/de.json'), true);
|
||||
\$es = json_decode(file_get_contents('resources/lang/es.json'), true);
|
||||
\$fr = json_decode(file_get_contents('resources/lang/fr.json'), true);
|
||||
|
||||
echo \" en: \" . count(\$en) . \" keys\n\";
|
||||
echo \" nl: \" . count(\$nl) . \" keys\n\";
|
||||
echo \" de: \" . count(\$de) . \" keys\n\";
|
||||
echo \" es: \" . count(\$es) . \" keys\n\";
|
||||
echo \" fr: \" . count(\$fr) . \" keys\n\";
|
||||
"
|
||||
echo ""
|
||||
|
||||
# Translate each language
|
||||
echo "Step 2: Translating to Dutch (nl)..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=nl --non-interactive --chunk=100 2>&1 | tee /tmp/translate-nl.log
|
||||
echo "Dutch translation complete!"
|
||||
sleep 5
|
||||
|
||||
echo ""
|
||||
echo "Step 3: Translating to German (de)..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=de --non-interactive --chunk=100 2>&1 | tee /tmp/translate-de.log
|
||||
echo "German translation complete!"
|
||||
sleep 5
|
||||
|
||||
echo ""
|
||||
echo "Step 4: Translating to Spanish (es)..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=es --non-interactive --chunk=100 2>&1 | tee /tmp/translate-es.log
|
||||
echo "Spanish translation complete!"
|
||||
sleep 5
|
||||
|
||||
echo ""
|
||||
echo "Step 5: Translating to French (fr)..."
|
||||
php artisan ai-translator:translate-json --source=en --locale=fr --non-interactive --chunk=100 2>&1 | tee /tmp/translate-fr.log
|
||||
echo "French translation complete!"
|
||||
|
||||
echo ""
|
||||
echo "=== TRANSLATION COMPLETE ==="
|
||||
echo ""
|
||||
|
||||
# Show final counts
|
||||
echo "Final key counts:"
|
||||
php -r "
|
||||
\$en = json_decode(file_get_contents('resources/lang/en.json'), true);
|
||||
\$nl = json_decode(file_get_contents('resources/lang/nl.json'), true);
|
||||
\$de = json_decode(file_get_contents('resources/lang/de.json'), true);
|
||||
\$es = json_decode(file_get_contents('resources/lang/es.json'), true);
|
||||
\$fr = json_decode(file_get_contents('resources/lang/fr.json'), true);
|
||||
|
||||
echo \" en: \" . count(\$en) . \" keys\n\";
|
||||
echo \" nl: \" . count(\$nl) . \" keys\n\";
|
||||
echo \" de: \" . count(\$de) . \" keys\n\";
|
||||
echo \" es: \" . count(\$es) . \" keys\n\";
|
||||
echo \" fr: \" . count(\$fr) . \" keys\n\";
|
||||
"
|
||||
|
||||
echo ""
|
||||
echo "Translation logs saved to:"
|
||||
echo " - /tmp/translate-nl.log"
|
||||
echo " - /tmp/translate-de.log"
|
||||
echo " - /tmp/translate-es.log"
|
||||
echo " - /tmp/translate-fr.log"
|
||||
echo ""
|
||||
echo "Don't forget to clear Laravel cache:"
|
||||
echo " php artisan config:clear && php artisan cache:clear"
|
||||
Reference in New Issue
Block a user