refactor: flatten Microsoft skills from nested to flat directory structure
Rewrote sync_microsoft_skills.py (v4) to use each SKILL.md's frontmatter 'name' field as the flat directory name under skills/, replacing the nested skills/official/microsoft/<lang>/<category>/<service>/ hierarchy. This fixes CI failures caused by the indexing, validation, and catalog scripts expecting skills/<id>/SKILL.md (depth 1). Changes: - Rewrite scripts/sync_microsoft_skills.py for flat output with collision detection - Update scripts/tests/inspect_microsoft_repo.py for flat name mapping - Update scripts/tests/test_comprehensive_coverage.py for name uniqueness checks - Delete skills/official/ nested directory - Add 129 Microsoft skills as flat directories (e.g. skills/azure-mgmt-botservice-dotnet/) - Move attribution files to docs/ (LICENSE-MICROSOFT, microsoft-skills-attribution.json) - Rebuild skills_index.json, CATALOG.md, README.md (845 total skills)
This commit is contained in:
286
skills/azure-ai-translation-ts/SKILL.md
Normal file
286
skills/azure-ai-translation-ts/SKILL.md
Normal file
@@ -0,0 +1,286 @@
|
||||
---
|
||||
name: azure-ai-translation-ts
|
||||
description: Build translation applications using Azure Translation SDKs for JavaScript (@azure-rest/ai-translation-text, @azure-rest/ai-translation-document). Use when implementing text translation, transliteration, language detection, or batch document translation.
|
||||
package: @azure-rest/ai-translation-text, @azure-rest/ai-translation-document
|
||||
---
|
||||
|
||||
# Azure Translation SDKs for TypeScript
|
||||
|
||||
Text and document translation with REST-style clients.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Text translation
|
||||
npm install @azure-rest/ai-translation-text @azure/identity
|
||||
|
||||
# Document translation
|
||||
npm install @azure-rest/ai-translation-document @azure/identity
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
TRANSLATOR_ENDPOINT=https://api.cognitive.microsofttranslator.com
|
||||
TRANSLATOR_SUBSCRIPTION_KEY=<your-api-key>
|
||||
TRANSLATOR_REGION=<your-region> # e.g., westus, eastus
|
||||
```
|
||||
|
||||
## Text Translation Client
|
||||
|
||||
### Authentication
|
||||
|
||||
```typescript
|
||||
import TextTranslationClient, { TranslatorCredential } from "@azure-rest/ai-translation-text";
|
||||
|
||||
// API Key + Region
|
||||
const credential: TranslatorCredential = {
|
||||
key: process.env.TRANSLATOR_SUBSCRIPTION_KEY!,
|
||||
region: process.env.TRANSLATOR_REGION!,
|
||||
};
|
||||
const client = TextTranslationClient(process.env.TRANSLATOR_ENDPOINT!, credential);
|
||||
|
||||
// Or just credential (uses global endpoint)
|
||||
const client2 = TextTranslationClient(credential);
|
||||
```
|
||||
|
||||
### Translate Text
|
||||
|
||||
```typescript
|
||||
import TextTranslationClient, { isUnexpected } from "@azure-rest/ai-translation-text";
|
||||
|
||||
const response = await client.path("/translate").post({
|
||||
body: {
|
||||
inputs: [
|
||||
{
|
||||
text: "Hello, how are you?",
|
||||
language: "en", // source (optional, auto-detect)
|
||||
targets: [
|
||||
{ language: "es" },
|
||||
{ language: "fr" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
if (isUnexpected(response)) {
|
||||
throw response.body.error;
|
||||
}
|
||||
|
||||
for (const result of response.body.value) {
|
||||
for (const translation of result.translations) {
|
||||
console.log(`${translation.language}: ${translation.text}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Translate with Options
|
||||
|
||||
```typescript
|
||||
const response = await client.path("/translate").post({
|
||||
body: {
|
||||
inputs: [
|
||||
{
|
||||
text: "Hello world",
|
||||
language: "en",
|
||||
textType: "Plain", // or "Html"
|
||||
targets: [
|
||||
{
|
||||
language: "de",
|
||||
profanityAction: "NoAction", // "Marked" | "Deleted"
|
||||
tone: "formal", // LLM-specific
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Get Supported Languages
|
||||
|
||||
```typescript
|
||||
const response = await client.path("/languages").get();
|
||||
|
||||
if (isUnexpected(response)) {
|
||||
throw response.body.error;
|
||||
}
|
||||
|
||||
// Translation languages
|
||||
for (const [code, lang] of Object.entries(response.body.translation || {})) {
|
||||
console.log(`${code}: ${lang.name} (${lang.nativeName})`);
|
||||
}
|
||||
```
|
||||
|
||||
### Transliterate
|
||||
|
||||
```typescript
|
||||
const response = await client.path("/transliterate").post({
|
||||
body: { inputs: [{ text: "这是个测试" }] },
|
||||
queryParameters: {
|
||||
language: "zh-Hans",
|
||||
fromScript: "Hans",
|
||||
toScript: "Latn",
|
||||
},
|
||||
});
|
||||
|
||||
if (!isUnexpected(response)) {
|
||||
for (const t of response.body.value) {
|
||||
console.log(`${t.script}: ${t.text}`); // Latn: zhè shì gè cè shì
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Detect Language
|
||||
|
||||
```typescript
|
||||
const response = await client.path("/detect").post({
|
||||
body: { inputs: [{ text: "Bonjour le monde" }] },
|
||||
});
|
||||
|
||||
if (!isUnexpected(response)) {
|
||||
for (const result of response.body.value) {
|
||||
console.log(`Language: ${result.language}, Score: ${result.score}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Document Translation Client
|
||||
|
||||
### Authentication
|
||||
|
||||
```typescript
|
||||
import DocumentTranslationClient from "@azure-rest/ai-translation-document";
|
||||
import { DefaultAzureCredential } from "@azure/identity";
|
||||
|
||||
const endpoint = "https://<translator>.cognitiveservices.azure.com";
|
||||
|
||||
// TokenCredential
|
||||
const client = DocumentTranslationClient(endpoint, new DefaultAzureCredential());
|
||||
|
||||
// API Key
|
||||
const client2 = DocumentTranslationClient(endpoint, { key: "<api-key>" });
|
||||
```
|
||||
|
||||
### Single Document Translation
|
||||
|
||||
```typescript
|
||||
import DocumentTranslationClient from "@azure-rest/ai-translation-document";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
|
||||
const response = await client.path("/document:translate").post({
|
||||
queryParameters: {
|
||||
targetLanguage: "es",
|
||||
sourceLanguage: "en", // optional
|
||||
},
|
||||
contentType: "multipart/form-data",
|
||||
body: [
|
||||
{
|
||||
name: "document",
|
||||
body: "Hello, this is a test document.",
|
||||
filename: "test.txt",
|
||||
contentType: "text/plain",
|
||||
},
|
||||
],
|
||||
}).asNodeStream();
|
||||
|
||||
if (response.status === "200") {
|
||||
await writeFile("translated.txt", response.body);
|
||||
}
|
||||
```
|
||||
|
||||
### Batch Document Translation
|
||||
|
||||
```typescript
|
||||
import { ContainerSASPermissions, BlobServiceClient } from "@azure/storage-blob";
|
||||
|
||||
// Generate SAS URLs for source and target containers
|
||||
const sourceSas = await sourceContainer.generateSasUrl({
|
||||
permissions: ContainerSASPermissions.parse("rl"),
|
||||
expiresOn: new Date(Date.now() + 24 * 60 * 60 * 1000),
|
||||
});
|
||||
|
||||
const targetSas = await targetContainer.generateSasUrl({
|
||||
permissions: ContainerSASPermissions.parse("rwl"),
|
||||
expiresOn: new Date(Date.now() + 24 * 60 * 60 * 1000),
|
||||
});
|
||||
|
||||
// Start batch translation
|
||||
const response = await client.path("/document/batches").post({
|
||||
body: {
|
||||
inputs: [
|
||||
{
|
||||
source: { sourceUrl: sourceSas },
|
||||
targets: [
|
||||
{ targetUrl: targetSas, language: "fr" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
// Get operation ID from header
|
||||
const operationId = new URL(response.headers["operation-location"])
|
||||
.pathname.split("/").pop();
|
||||
```
|
||||
|
||||
### Get Translation Status
|
||||
|
||||
```typescript
|
||||
import { isUnexpected, paginate } from "@azure-rest/ai-translation-document";
|
||||
|
||||
const statusResponse = await client.path("/document/batches/{id}", operationId).get();
|
||||
|
||||
if (!isUnexpected(statusResponse)) {
|
||||
const status = statusResponse.body;
|
||||
console.log(`Status: ${status.status}`);
|
||||
console.log(`Total: ${status.summary.total}`);
|
||||
console.log(`Success: ${status.summary.success}`);
|
||||
}
|
||||
|
||||
// List documents with pagination
|
||||
const docsResponse = await client.path("/document/batches/{id}/documents", operationId).get();
|
||||
const documents = paginate(client, docsResponse);
|
||||
|
||||
for await (const doc of documents) {
|
||||
console.log(`${doc.id}: ${doc.status}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Get Supported Formats
|
||||
|
||||
```typescript
|
||||
const response = await client.path("/document/formats").get();
|
||||
|
||||
if (!isUnexpected(response)) {
|
||||
for (const format of response.body.value) {
|
||||
console.log(`${format.format}: ${format.fileExtensions.join(", ")}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Key Types
|
||||
|
||||
```typescript
|
||||
// Text Translation
|
||||
import type {
|
||||
TranslatorCredential,
|
||||
TranslatorTokenCredential,
|
||||
} from "@azure-rest/ai-translation-text";
|
||||
|
||||
// Document Translation
|
||||
import type {
|
||||
DocumentTranslateParameters,
|
||||
StartTranslationDetails,
|
||||
TranslationStatus,
|
||||
} from "@azure-rest/ai-translation-document";
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Auto-detect source** - Omit `language` parameter to auto-detect
|
||||
2. **Batch requests** - Translate multiple texts in one call for efficiency
|
||||
3. **Use SAS tokens** - For document translation, use time-limited SAS URLs
|
||||
4. **Handle errors** - Always check `isUnexpected(response)` before accessing body
|
||||
5. **Regional endpoints** - Use regional endpoints for lower latency
|
||||
Reference in New Issue
Block a user