tencentcloud/tmt
Tencent Cloud TMT SDK for PHP provides a client for Tencent Cloud Machine Translation, enabling text translation, language detection, and related API calls from PHP applications. Install via Composer and configure credentials to integrate translation features into Laravel or other PHP projects.
tencentcloud/tmt is not a Laravel package — it's the per-product, machine-generated PHP client for Tencent Cloud's Machine Translation (TMT) API, version 20180321. There is no service provider, no facade, no publishable config, and 0 dependents because virtually everyone just installs the monolith (tencentcloud/tencentcloud-sdk-php) instead. Judge it by the API behind it: a stable, cheap, character-billed translation service.
composer require tencentcloud/tmt
Where to look first: src/TencentCloud/Tmt/V20180321/TmtClient.php is your real documentation — every public method is one API action (TextTranslate, TextTranslateBatch, ImageTranslate, SpeechTranslate, LanguageDetect). Each action has a matching Models/*Request and *Response class; the request setters are the parameter list and the response getters are the return shape, so you rarely need the web docs.
First use case — translating user-generated or admin-authored text (TextTranslate covers ~90% of usage):
// config/services.php
'tencent' => [
'tmt' => [
'secret_id' => env('TENCENT_TMT_SECRET_ID'),
'secret_key' => env('TENCENT_TMT_SECRET_KEY'),
'region' => env('TENCENT_TMT_REGION', 'ap-guangzhou'),
],
],
use TencentCloud\Common\Credential;
use TencentCloud\Common\Profile\ClientProfile;
use TencentCloud\Common\Profile\HttpProfile;
use TencentCloud\Tmt\V20180321\TmtClient;
use TencentCloud\Tmt\V20180321\Models\TextTranslateRequest;
$credential = new Credential(
config('services.tencent.tmt.secret_id'),
config('services.tencent.tmt.secret_key')
);
$httpProfile = new HttpProfile();
$httpProfile->setEndpoint('tmt.tencentcloudapi.com');
$httpProfile->setReqTimeout(10);
$clientProfile = new ClientProfile();
$clientProfile->setHttpProfile($httpProfile);
$client = new TmtClient($credential, config('services.tencent.tmt.region'), $clientProfile);
$req = new TextTranslateRequest();
$req->setSourceText('Hello world');
$req->setSource('en'); // or 'auto'
$req->setTarget('zh');
$req->setProjectId(0);
$res = $client->TextTranslate($req);
$res->getTargetText(); // 你好世界
$res->getSource(); // source language (detected when 'auto')
$res->getRequestId(); // always log this
Create the API key as a CAM sub-user scoped to TMT actions only — never the root account key, and never commit it.
Wrap it once, then never import it again. The generated classes are ugly and voluminous; keep them out of controllers and models.
interface Translator {
public function translate(string $text, string $from, string $to): string;
}
final class TencentTranslator implements Translator { /* the block above */ }
Bind Translator and a lazy TmtClient singleton in AppServiceProvider:
$this->app->singleton(TmtClient::class, fn () => /* build with config() */);
$this->app->singleton(Translator::class, TencentTranslator::class);
The client is just credentials + profile objects — safe to reuse for the life of a request or queue worker, and building it lazily avoids paying for it on requests that never translate.
Cache-aside inside the adapter — the single biggest cost lever. Billing is per character, and the same input always yields the same output, so cache before calling and write back after:
$key = 'tmt:'.sha1("{$from}|{$to}|{$text}");
return Cache::remember($key, now()->addDays(90), fn () => $this->callApi($text, $from, $to));
Callers get caching for free and can't accidentally bypass it. Short-circuit empty/whitespace strings before the key lookup.
Batch aggressively, off the request cycle. TextTranslateBatch takes SourceTextList and returns a TargetTextList in the same order (check the current per-request item cap). Translating a page of 20 records as 20 HTTP calls is both slow and rate-limit bait. Push bulk work — backfilling a catalog, translating a newly imported CSV — into a queued job.
Translate on write, not on read. Store the translated copy alongside the source when content is saved, in a job. If you must translate on read (e.g. arbitrary user content), keep the timeout at 2–3s and serve the source text on failure — translation should degrade, not 500.
Centralize language mapping. Your app stores zh-Hans, pt_BR, en-GB; TMT wants zh, pt, en. Map in one place, and pin the source language whenever you know it — auto costs an extra detection pass and is less predictable for static UI strings.
Instrument for cost reconciliation. Log RequestId, source/target, mb_strlen of the input, latency, and cache hit/miss. Characters are the billing unit; a small tmt_usage table lets you reconcile against the Tencent console and spot runaway loops (a retranslation job re-firing every minute is a real incident).
Per-tenant quotas. On multi-tenant apps, put a counter/limiter in front of the adapter keyed by tenant. One customer's import loop shouldn't exhaust the shared account quota.
Testing: don't hit the network. Bind a fake Translator in the container for feature tests. To unit-test your adapter's mapping and chunking logic, inject TmtClient rather than new-ing it, and subclass/override TextTranslate in a test double — there is no SDK-provided fake.
Region is a config decision, not a constant. Pick the closest region that offers TMT; it affects latency and may affect data-residency obligations. Keep it in env, not in code.
tencentcloud/tmt is a per-product split of tencentcloud/tencentcloud-sdk-php. If anything else in the tree already pulls the monolith, you can end up with two copies of TencentCloud\Common\* and TencentCloud\Tmt\* — duplicate class definitions, autoload ambiguity, or version drift producing baffling "class not found"/"method not found" errors. Decide on one path (monolith-only is usually simpler) and pin it.TencentCloudSDKException exposes getErrorCode(), getErrorMessage(), getRequestId(). Catch it at the adapter boundary, log the RequestId, and rethrow as your own exception type — never let the SDK's exception escape into a controller.RequestLimitExceeded-style), with exponential backoff plus jitter and a hard attempt cap. Never wrap the call in a naive for loop: each attempt is billed, and your usage log will drift from reality.date before debugging code.TextTranslate rejects very long strings (on the order of a couple thousand characters, language-dependent). Chunk on sentence boundaries with mb_* functions — byte-offset chunking splits multibyte characters and produces garbled output at the seams.null for fields the API didn't populate. Convert to your own DTO immediately so the nullability is explicit and testable.config:cache freezes env(). Read credentials via config('services.tencent.tmt.*') inside the app, or you'll get nulls in production only.ProjectId defaults to 0. Set it explicitly; leaving it out is fine in most setups but some console-configured projects expect it.setHttpClient() / profile proxy settings. Don't subclass generated code; build the interface + adapter + fake and let the SDK be an implementation detail you can swap for another MT vendor later.How can I help you explore Laravel packages today?