Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions samples/react-command-document-translation/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Dependency directories
node_modules/
jspm_packages/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test
.env.local

# SPFx build output
SPFx/dist/
SPFx/lib/
SPFx/temp/
SPFx/coverage/
SPFx/release/
SPFx/sharepoint/

# Azure Function build output
AzureFunction/**/bin/
AzureFunction/**/obj/
AzureFunction/**/.vs/
AzureFunction/**/*.user
AzureFunction/**/*.suo
AzureFunction/**/.vscode/
AzureFunction/**/local.settings.json

# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# IDE
.vscode/
.idea/
*.swp
*.swo
*~

# Azure Storage Emulator
__blobstorage__/
__queuestorage__/
__azurite_db*__.json
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>8fd684cc-26c3-59e2-940e-25c29bf8bbf1</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<Content Include="host.json" />
<Content Include="local.settings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Azure.Data.Tables" Version="12.11.0" />
<PackageReference Include="Azure.Identity" Version="1.13.2" />
<PackageReference Include="Azure.Messaging.ServiceBus" Version="7.18.1" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.26.0-beta.1" />
<PackageReference Include="FluentValidation" Version="12.0.0" />
<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.23.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="2.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.0.1" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.ServiceBus" Version="5.23.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs" Version="6.7.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.0-preview.7.25380.108" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.8" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="PnP.Core" Version="1.15.122-nightly" />
<PackageReference Include="PnP.Core.Auth" Version="1.15.122-nightly" />
<PackageReference Include="Polly" Version="8.6.2" />
<PackageReference Include="System.ComponentModel" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35825.156 d17.13
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocumentTranslationApp", "DocumentTranslationApp.csproj", "{30B344B5-EB42-B184-1746-0AC0D4B9EB7D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{30B344B5-EB42-B184-1746-0AC0D4B9EB7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{30B344B5-EB42-B184-1746-0AC0D4B9EB7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{30B344B5-EB42-B184-1746-0AC0D4B9EB7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{30B344B5-EB42-B184-1746-0AC0D4B9EB7D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D5EAEA8B-E51D-4ADD-B867-EB32C21D5380}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace DocumentTranslationApp.Models;

public class SpfxTranslationRequest
{
public required string SiteUrl { get; set; }
public required List<DocumentInfo> Documents { get; set; }
public required TranslationOptions Options { get; set; }
public RequestContext? Context { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
namespace DocumentTranslationApp.Models;

public enum JobStatus
{
Pending,
Running,
Processing,
Completed,
Failed,
Cancelled,
}

public class TranslationRequest
{
public required string SiteUrl { get; set; }
public required List<DocumentInfo> Documents { get; set; }
public required TranslationOptions Options { get; set; }
public RequestContext? Context { get; set; }
public string? UserAccessToken { get; set; } // Token from SPFx for On-Behalf-Of flow
}

public class DocumentInfo
{
public required string Id { get; set; }
public required string Name { get; set; }
public required string ServerRelativeUrl { get; set; }
public long Size { get; set; }
public required string FileType { get; set; }
public bool IsSupported { get; set; }
public string? ErrorMessage { get; set; }
}

public class TranslationOptions
{
public string? SourceLanguage { get; set; } // Optional for auto-detect
public required List<string> TargetLanguages { get; set; }
}

public class RequestContext
{
public required string UserId { get; set; }
public required string WebId { get; set; }
public required string TenantId { get; set; } // Required for On-Behalf-Of flow
public required string ListId { get; set; }
}

public class TranslationResult
{
public bool Success { get; set; }
public required string JobId { get; set; }
public required string Message { get; set; }
public int EstimatedDocuments { get; set; }
public List<string>? Errors { get; set; }
}

public class TranslatedDocument
{
public required string OriginalName { get; set; }
public required string TargetLanguage { get; set; }
public required string TranslatedName { get; set; }
public required string ServerRelativeUrl { get; set; }
public int CharacterCount { get; set; }
}

public class FailedDocument
{
public required string Name { get; set; }
public required string TargetLanguage { get; set; }
public required string Error { get; set; }
}

public class TranslationJobStatus
{
public required string JobId { get; set; }
public JobStatus Status { get; set; }
public int Progress { get; set; }
public string? Message { get; set; }
public List<TranslatedDocument> CompletedDocuments { get; set; } = new();
public List<FailedDocument> FailedDocuments { get; set; } = new();
public int TotalDocuments { get; set; }
public long TotalCharacterCharged { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? CompletedAt { get; set; }
}

public class ProcessingJob
{
public required string JobId { get; set; }
public required string SiteUrl { get; set; }
public required List<DocumentInfo> Documents { get; set; }
public required TranslationOptions Options { get; set; }
public RequestContext? Context { get; set; }
public JobStatus Status { get; set; }
public int Progress { get; set; }
public string? Message { get; set; }
public List<TranslatedDocument> CompletedDocuments { get; set; } = new();
public List<FailedDocument> FailedDocuments { get; set; } = new();
public int TotalDocuments { get; set; }
public long TotalCharacterCharged { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? CompletedAt { get; set; }
public string? ErrorMessage { get; set; }
public string? UserAccessToken { get; set; } // Store user token for On-Behalf-Of flow
public string? AzureTranslationJobId { get; set; } // Azure Document Translation API job ID
public int RetryCount { get; set; } = 0;
public int MaxRetries { get; set; } = 3;
}

public class DocumentContent
{
public required string FileName { get; set; }
public required byte[] Bytes { get; set; }
public required string FileType { get; set; }
public required string ContentType { get; set; }
}

// Azure Document Translation API Models
public class AzureTranslationBatchRequest
{
public required List<AzureTranslationInput> Inputs { get; set; }
}

public class AzureTranslationInput
{
public required AzureTranslationSource Source { get; set; }
public required List<AzureTranslationTarget> Targets { get; set; }
}

public class AzureTranslationSource
{
public required string SourceUrl { get; set; }
public string? Language { get; set; } // Optional for auto-detect
public string StorageSource { get; set; } = "AzureBlob";
}

public class AzureTranslationTarget
{
public required string TargetUrl { get; set; }
public required string Language { get; set; }
public string StorageSource { get; set; } = "AzureBlob";
}

public class AzureTranslationBatchResponse
{
public required string Id { get; set; }
public required DateTime CreatedDateTimeUtc { get; set; }
public required DateTime LastActionDateTimeUtc { get; set; }
public required string Status { get; set; }
public required AzureTranslationSummary Summary { get; set; }
}

public class AzureTranslationSummary
{
public int Total { get; set; }
public int Failed { get; set; }
public int Success { get; set; }
public int InProgress { get; set; }
public int NotYetStarted { get; set; }
public int Cancelled { get; set; }
public long TotalCharacterCharged { get; set; }
}

public class AzureBlobItem
{
public required string Name { get; set; }
public required MemoryStream Stream { get; set; }
public required string Url { get; set; }
}

public class DocumentTranslationJobStatus
{
public string JobId { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
public DateTime CreatedDateTime { get; set; }
public DateTime LastUpdatedDateTime { get; set; }
public string? ExpirationDateTime { get; set; }
public List<DocumentTranslationResult> Results { get; set; } = new();
public List<DocumentTranslationError> Errors { get; set; } = new();

public long TotalCharacterCharged { get; set; }

// Task summary information
public int TasksCompleted { get; set; }
public int TasksFailed { get; set; }
public int TasksInProgress { get; set; }
public int TasksTotal { get; set; }

// Top-level error information (for ValidationFailed status)
public string? ErrorCode { get; set; }
public string? ErrorMessage { get; set; }
public string? ErrorTarget { get; set; }

}

public class DocumentTranslationResult
{
public required string DocumentId { get; set; }
public required string Status { get; set; }
public string? Path { get; set; }
public string? TranslatedDocumentLocation { get; set; }
}

public class AppInfo
{
public required string ClientId { get; set; }
public required string ClientSecret { get; set; }
}

// Configuration Models
public class TranslationConfiguration
{
public int MaxFileSizeBytes { get; set; } = 40 * 1024 * 1024; // 40MB (Azure limit)
public int MaxDocumentsPerJob { get; set; } = 50;
public int JobTimeoutMinutes { get; set; } = 60;
public List<string> SupportedFileTypes { get; set; } = new()
{
".docx", ".xlsx", ".pptx", ".pdf", ".html", ".htm",
".txt", ".md", ".msg", ".odt", ".ods", ".odp"
};
public string SourceContainerName { get; set; } = "translation-source";
public string TargetContainerPrefix { get; set; } = "translation-target-";
public bool DeleteBlobsAfterProcessing { get; set; } = true;
public int BlobSasExpiryHours { get; set; } = 2;
public int StatusPollingIntervalSeconds { get; set; } = 5;
}

/// <summary>
/// Represents an error that occurred during document translation.
/// </summary>
public class DocumentTranslationError
{
public string Code { get; set; } = string.Empty;
public string Message { get; set; } = string.Empty;
public string? Target { get; set; }
}
Loading
Loading