Bblslug is a versatile translation tool that can be used as both a CLI utility and a PHP library.
It leverages LLM-based APIs to translate plain text, HTML and JSON while preserving structure, code blocks, and URLs via placeholder filters.
APIs supported:
- Anthropic (Claude):
anthropic:claude-haiku-3.5- Claude Haiku 3.5 (latest)anthropic:claude-opus-4- Claude Opus 4 (20250514)anthropic:claude-sonnet-4- Claude Sonnet 4 (20250514)
- DeepL:
deepl:free- DeepL free tierdeepl:pro- DeepL pro tier
- Google (Gemini):
google:gemini-2.5-flash- Gemini 2.5 Flashgoogle:gemini-2.5-flash-lite- Gemini 2.5 Flash Litegoogle:gemini-2.5-pro- Gemini 2.5 Progoogle:gemini-2.0-flash- Gemini 2.0 Flash
- OpenAI (GPT):
openai:gpt-5- OpenAI GPT-5openai:gpt-5-mini- OpenAI GPT-5 Miniopenai:gpt-5-nano- OpenAI GPT-5 Nanoopenai:gpt-4o- OpenAI GPT-4oopenai:gpt-4o-mini- OpenAI GPT-4o Miniopenai:gpt-4- OpenAI GPT-4openai:gpt-4-turbo- OpenAI GPT-4 Turbo
- Yandex:
yandex:gpt-lite- YandexGPT Liteyandex:gpt-pro- YandexGPT Proyandex:gpt-32k- YandexGPT Pro 32K
- X.ai:
xai:grok-4- Grok 4xai:grok-3- Grok 3xai:grok-3-mini- Grok 3 Mini
- Supports HTML, JSON and plain text (
--format=text|html|json) - Placeholder-based protection with filters:
html_pre,html_code,url, etc. - Model selection via
--model=vendor:name - Fully configurable backend registry (via
resources/models.yaml) - Dry-run mode to preview placeholders without making API calls
- Variables (
--variables) to send or override model-specific options - Verbose mode (
--verbose) to print request previews - Can be invoked as a CLI tool or embedded in PHP code
- Validation of container syntax for HTML or JSON; disable with
--no-validate
composer require habr/bblslug
chmod +x vendor/bin/bblslug-
Always specify a model with
--model=vendor:nameoption. -
Export your API key(s) before running:
export ANTHROPIC_API_KEY=...
export DEEPL_FREE_API_KEY=...
export DEEPL_PRO_API_KEY=...
export GOOGLE_API_KEY=...
export OPENAI_API_KEY=...
export YANDEX_API_KEY=... && export YANDEX_FOLDER_ID=...
export XAI_API_KEY=...NB! Some vendors require additional parameters, e.g. YANDEX_FOLDER_ID.
- Input / output:
- If
--sourceis omitted, Bblslug reads from STDIN. - If
--translatedis omitted, Bblslug writes to STDOUT.
- Optional proxy:
To route requests through a proxy (e.g. HTTP or SOCKS5), use the --proxy option or set the BBLSLUG_PROXY environment variable:
# using CLI flag
vendor/bin/bblslug --proxy="http://localhost:8888" ...
# or set it globally
export BBLSLUG_PROXY="socks5h://127.0.0.1:9050"This works for all HTTP requests and supports authentication (http://user:pass@host:port).
vendor/bin/bblslug --list-modelsvendor/bin/bblslug --list-promptsvendor/bin/bblslug \
--model=vendor:name \
--format=html \
--source=input.html \
--translated=output.htmlvendor/bin/bblslug \
--model=vendor:name \
--format=json \
--source=input.json \
--translated=output.jsonvendor/bin/bblslug \
--model=vendor:name \
--format=html \
--source=input.html \
--translated=output.html \
--filters=url,html_code,html_prevendor/bin/bblslug \
--model=vendor:name \
--format=html \
--source=input.html \
--translated=output.html \
--target-lang=EN \
--source-lang=DE \
--context="Translate as a professional technical translator"vendor/bin/bblslug \
--model=vendor:name \
--format=text \
--variables=foo=bar,foo2=bar2 \
--source=in.txt \
--translated=out.txtvendor/bin/bblslug \
--model=vendor:name \
--format=html \
--source=input.html \
--translated=out.html \
--prompt-key=smart-legalvendor/bin/bblslug \
--model=vendor:name \
--format=text \
--filters=url \
--source=input.txt \
--dry-runvendor/bin/bblslug \
--model=vendor:name \
--format=html \
--verbose \
--source=input.html \
--translated=out.htmlcat input.txt | vendor/bin/bblslug \
--model=vendor:name \
--format=text \
--translated=out.txtecho "Hello world" | vendor/bin/bblslug \
--model=vendor:name \
--format=text > translated.outFor HTML and JSON formats, Bblslug performs basic syntax validation before and after translation. To skip this step, add:
vendor/bin/bblslug \
--model=vendor:name \
--format=html \
--no-validate \
--source=input.html \
--translated=out.html-
Usage metrics
After each translation (when not in dry-run), Bblslug prints to stderr a summary of consumed usage metrics, for example:
Usage metrics: Tokens: Total: 1074 ----------------- Prompt: 631 Completion: 443
You can embed Bblslug in your PHP project.
- Install:
composer require habr/bblslug- Require & Import:
require 'vendor/autoload.php';
use Bblslug\Bblslug;Text translation function example:
$text = file_get_contents('input.html');
$result = Bblslug::translate(
apiKey: getenv('MODEL_API_KEY'), // API key for the chosen model
format: 'html', // 'text', 'html' of 'json'
modelKey: 'vendor:model', // Model identifier (e.g. deepl:free, openai:gpt-4o, etc.)
text: $text, // Source text to be translated
// optional:
// Additional context/prompt pass to model
context: 'Translate as a professional technical translator',
filters: ['url','html_code'], // List of placeholder filters
promptKey: 'translator', // which prompt template to use
proxy: getenv('BBLSLUG_PROXY'), // Optional proxy URI (http://..., socks5h://...)
sourceLang: 'DE', // Source language code (optional; autodetect if null)
targetLang: 'EN', // Target language code (optional; default from driver settings)
validate: false, // perform or skip syntax validation for container formats
variables: ['foo'=>'bar'], // model-specific overrides
verbose: true, // If true, returns debug request/response
);
echo $result['result'];Result structure:
[
'original' => string, // Original input
'prepared' => string, // After placeholder filters
'result' => string, // Translated result
'httpStatus' => int, // HTTP status
'debugRequest' => string, // Request debug
'debugResponse' => string, // Response debug
'rawResponseBody' => string, // Response body
'consumed' => [ // Normalized usage metrics
'tokens' => [
'total' => int, // Total tokens consumed
'breakdown' => [ // Per-type breakdown
'prompt' => int, // Name depeds of model
'completion' => int, // Name depeds of model
],
],
// additional categories if supported by the model...
'lengths' => [ // Text length statistics
'original' => int, // - original text
'prepared' => int, // - after placeholder filters
'translated' => int, // - returned translated text
],
'filterStats' => [ // Placeholder stats
['filter'=>'url','count'=>3], …
],
]$modelsByVendor = Bblslug::listModels();
foreach ($modelsByVendor as $vendor => $models) {
echo "Vendor: {$vendor}\n";
foreach ($models as $key => $config) {
printf(" - %s: %s\n", $key, $config['notes'] ?? '(no notes)');
}
}Returns an array like:
[
'deepl' => ['deepl:free' => […], 'deepl:pro' => […]],
'openai' => ['openai:gpt-4' => […], …],
…
]$prompts = Bblslug::listPrompts();
foreach ($prompts as $key => $info) {
$formats = implode(', ', $info['formats']);
echo "{$key} ({$formats})";
if (! empty($info['notes'])) {
echo " – {$info['notes']}";
}
echo "\n";
}Returns an array like:
[
'translator' => [
'formats' => ['text', 'html', 'json'],
'notes' => 'professional translator template',
],
'legal' => [
'formats' => ['text'],
'notes' => 'legal-style translator template',
],
// …
]try {
$res = Bblslug::translate(...);
} catch (\InvalidArgumentException $e) {
// invalid model, missing API key, etc.
} catch (\RuntimeException $e) {
// HTTP error, parse failure, driver-specific error
}You can find sample input files under the samples/ directory.
This project is licensed under the MIT License – see the LICENSE file for details.