Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XLIFF Sync - New Build with Translations command #122

Merged
merged 14 commits into from
Aug 16, 2024
Merged
9 changes: 4 additions & 5 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build",
"isDefault": true
}
"group": "build",
"label": "npm: watch",
"detail": "tsc -watch -p ./"
}
]
}
}
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## [1.5.0] 15-08-2024

* New command `xliffSync.buildWithTranslations` which first builds the project in the currently open workspace/project folder, and directly afterwards synchronizes all .xlf translation files in the workspace/project folder.
* New setting `xliffSync.defaultLanguages` that can be used to specify the languages that should automatically be used for the translation file generation.
* New setting `xliffSync.buildCommandToExecute` that specifies the build command to execute for the `xliffSync.buildWithTranslations` command

### Thank You

* **[FlorianNoeverGOB](https://github.com/FlorianNoeverGOB)** for your pull request to add the `xliffSync.buildWithTranslations` command (Pull Request [#122](https://github.com/rvanbekkum/vsc-xliff-sync/pull/122))

## [1.4.0] 30-12-2022

* New setting `xliffSync.useSelfClosingTags` that can be used to specify whether to use self-closing tags in the XLIFF target translation files. (GitHub issue [#97](https://github.com/rvanbekkum/vsc-xliff-sync/issues/97))
Expand Down
193 changes: 152 additions & 41 deletions README.md

Large diffs are not rendered by default.

44 changes: 34 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "xliff-sync",
"displayName": "XLIFF Sync",
"description": "A tool to keep XLIFF translation files in sync.",
"version": "1.4.0",
"version": "1.5.3",
"publisher": "rvanbekkum",
"repository": {
"type": "git",
Expand All @@ -25,21 +25,13 @@
"theme": "dark"
},
"engines": {
"vscode": "^1.47.0"
"vscode": "^1.75.0"
},
"categories": [
"Other"
],
"main": "./out/extension",
"activationEvents": [
"onCommand:xliffSync.checkForMissingTranslations",
"onCommand:xliffSync.checkForNeedWorkTranslations",
"onCommand:xliffSync.createNewTargetFiles",
"onCommand:xliffSync.findNextMissingTarget",
"onCommand:xliffSync.findNextNeedsWorkTarget",
"onCommand:xliffSync.importTranslationsFromFiles",
"onCommand:xliffSync.synchronizeFile",
"onCommand:xliffSync.synchronizeSources",
"workspaceContains:**/*.xlf",
"workspaceContains:**/*.xlf2"
],
Expand Down Expand Up @@ -383,6 +375,28 @@
"default": "TargetLanguageCode",
"description": "Specifies which target language to use by default in the XLIFF Sync snippets.",
"scope": "resource"
},
"xliffSync.defaultLanguages": {
rvanbekkum marked this conversation as resolved.
Show resolved Hide resolved
"type": "array",
"items": {
"type": "string"
},
"examples": [
"en-US",
"de-DE"
],
"default": [],
"uniqueItems": true,
"description": "Specifies the languages that should automatically be used for the translation file generation. \nExamples: \"en-US\", \"de-DE\".",
"order": 0,
"scope": "resource"
},
"xliffSync.buildCommandToExecute": {
"type": "string",
"default": "al.package",
"description": "Specifies the build command to execute when building with translations.",
"order": 1,
"scope": "resource"
}
}
},
Expand Down Expand Up @@ -426,6 +440,11 @@
"command": "xliffSync.importTranslationsFromFiles",
"title": "Import Translations from File(s)",
"category": "XLIFF"
},
{
"command": "xliffSync.buildWithTranslations",
"title": "Build with Translations",
"category": "XLIFF"
}
],
"keybindings": [
Expand All @@ -446,6 +465,11 @@
"command": "xliffSync.findNextNeedsWorkTarget",
"key": "alt+x w",
"when": "resourceExtname == .xlf"
},
{
"command": "xliffSync.buildWithTranslations",
"key": "ctrl+shift+t",
"when": "workspaceFolderCount > 0"
}
],
"menus": {
Expand Down
Binary file added resources/alParseFromDevNoteSnippet.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified resources/xliffSync_commandPaletteCommands.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/xliffSync_defaultLanguages.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 8 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
} from 'vscode';

import { registerSnippets } from './features/snippets';
import { synchronizeFiles, synchronizeWithSelectedFile, createNewTargetFiles } from './features/trans-sync';
import { synchronizeFiles, synchronizeWithSelectedFile, createNewTargetFiles, buildWithTranslations } from './features/trans-sync';
import { XliffTranslationChecker } from './features/trans-check';
import { XliffTranslationImport } from './features/trans-import';

Expand All @@ -13,7 +13,7 @@ export function activate(context: ExtensionContext) {
new XliffTranslationImport(context);

context.subscriptions.push(
commands.registerCommand('xliffSync.createNewTargetFiles', async() => {
commands.registerCommand('xliffSync.createNewTargetFiles', async () => {
createNewTargetFiles();
})
);
Expand All @@ -35,6 +35,12 @@ export function activate(context: ExtensionContext) {
})
);

context.subscriptions.push(
commands.registerCommand('xliffSync.buildWithTranslations', async () => {
buildWithTranslations();
})
);

registerSnippets();
}

Expand Down
44 changes: 31 additions & 13 deletions src/features/tools/files-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,30 +51,48 @@ export class FilesHelper {
let uris: Uri[] = [];

if (fileType) {
uris = (await FilesHelper.findTranslationFiles(fileType, workspaceFolder)) || [];
uris = (await FilesHelper.findTranslationFiles(fileType, workspaceFolder)) || [];
}

if (!uris.length) {
fileType = await window.showQuickPick(['xlf', 'xlf2'], {
placeHolder: 'Translation file type',
});
fileType = await window.showQuickPick(['xlf', 'xlf2'], {
placeHolder: 'Translation file type',
});

if (fileType) {
uris = (await FilesHelper.findTranslationFiles(fileType, workspaceFolder)) || [];
if (fileType) {
uris = (await FilesHelper.findTranslationFiles(fileType, workspaceFolder)) || [];

if (uris.length) {
workspace.getConfiguration('xliffSync', workspaceFolder?.uri).update('fileType', fileType);
}
if (uris.length) {
workspace.getConfiguration('xliffSync', workspaceFolder?.uri).update('fileType', fileType);
}
}
}

if (!uris.length) {
throw new Error(`No translation file found (Workspace: "${workspaceFolder?.name}").`);
throw new Error(`No translation file found (Workspace: "${workspaceFolder?.name}").`);
}

return uris;
}

/**
* Checks if translation files exist in the opened workspace
*
* @param {WorkspaceFolder} workspaceFolder The folder to restrict the search to.
*
* @returns A boolean that specifies whether translation files exist in the current workspace.
*/
public static async xliffFilesExist(workspaceFolder?: WorkspaceFolder): Promise<boolean> {
let fileType: string | undefined = workspace.getConfiguration('xliffSync', workspaceFolder?.uri)['fileType'];
let uris: Uri[] = [];

if (fileType) {
uris = (await FilesHelper.findTranslationFiles(fileType, workspaceFolder)) || [];
}

return uris.length !== 0;
}

/**
* Retrieves the base/source/generated XLIFF file from a collection of XLIFF file URIs.
* Also prompts the user to specify a base file, if this wasn't done already.
Expand Down Expand Up @@ -114,11 +132,11 @@ export class FilesHelper {
if (xliffUris.length > 1) {
const fsPaths = xliffUris.map((uri) => uri.fsPath);
const sourcePath = await window.showQuickPick(fsPaths, {
placeHolder: 'Select the base XLIFF file',
placeHolder: 'Select the base XLIFF file',
});

if (!sourcePath) {
return undefined;
return undefined;
}

sourceUri = xliffUris.find((uri) => uri.fsPath === sourcePath)!;
Expand Down Expand Up @@ -168,7 +186,7 @@ export class FilesHelper {
}

public static getTranslationFileExtensions(fileType: string): string {
switch(fileType) {
switch (fileType) {
case 'xlf':
return 'xlf';
case 'xlf2':
Expand Down
2 changes: 1 addition & 1 deletion src/features/trans-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ function checkForOptionMemberLeadingSpacesMismatch(sourceText: string, translati
return false;
}

function getConsecutiveSpacesMatchesFromText(textToCheck: string): RegExpMatchArray {
function getConsecutiveSpacesMatchesFromText(textToCheck: string): RegExpMatchArray | [] {
return (textToCheck.match(/\s\s+/g) || []);
}

Expand Down
54 changes: 45 additions & 9 deletions src/features/trans-sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ import {
window,
workspace,
WorkspaceConfiguration,
WorkspaceFolder
WorkspaceFolder,
commands
} from 'vscode';

import { FilesHelper, LanguageHelper, WorkspaceHelper } from './tools';
Expand Down Expand Up @@ -73,10 +74,16 @@ export async function createNewTargetFiles() {

let uris: Uri[] = await FilesHelper.getXliffFileUris(workspaceFolder);
let sourceUri: Uri = await FilesHelper.getXliffSourceFile(uris, workspaceFolder);
await executeCreateNewTargetFiles(workspaceFolder, sourceUri);
}

async function executeCreateNewTargetFiles(workspaceFolder: WorkspaceFolder | undefined, sourceUri: Uri) {
const fileType: string | undefined = workspace.getConfiguration('xliffSync', workspaceFolder?.uri)['fileType'];
const targetLanguages: string[] | undefined = await selectNewTargetLanguages(fileType!, true);
if (!targetLanguages) {
let targetLanguages: string[] | undefined = workspace.getConfiguration('xliffSync', workspaceFolder?.uri)['defaultLanguages'];
if (!targetLanguages || targetLanguages.length === 0) {
targetLanguages = await selectNewTargetLanguages(fileType!, true);
}
if (!targetLanguages || targetLanguages.length === 0) {
return;
}

Expand All @@ -85,6 +92,35 @@ export async function createNewTargetFiles() {
}
}

export async function buildWithTranslations() {
return await window.withProgress({
location: ProgressLocation.Notification,
title: `Building with Translations`,
cancellable: false
}, async progress => {
progress.report({ message: "Initializing..." });
let workspaceFolders: WorkspaceFolder[] | undefined = await WorkspaceHelper.getWorkspaceFolders(false);
let workspaceFolder: WorkspaceFolder | undefined = workspaceFolders?.[0];
if (!workspaceFolder) {
throw new Error(`No workspace found for active file.`);
}

// Execute the build command (Ctrl + Shift + B)
progress.report({ message: "Executing build command..." });
const buildCommand: string | undefined = workspace.getConfiguration('xliffSync', workspaceFolder.uri)['buildCommandToExecute'];
if (!buildCommand) {
throw new Error(`No build command specified`);
}
await commands.executeCommand(buildCommand);

// Execute the xliffSync.synchronizeSources command
progress.report({ message: "Synchronizing Translation Units..." });
await synchronizeFilesInWorkspace(true, workspaceFolder);

progress.report({ message: "Build and translation synchronization complete!" });
});
}

/**
* Synchronize translation files in a workspace.
*
Expand Down Expand Up @@ -188,9 +224,9 @@ async function selectNewTargetLanguages(fileType: string, multiSelectAllowed: bo
var enterCustomLabel = '$(pencil) Enter custom...';
var altActions = [];
if (multiSelectAllowed) {
altActions.push({label: multiSelectLabel});
altActions.push({ label: multiSelectLabel });
}
altActions.push({label: enterCustomLabel});
altActions.push({ label: enterCustomLabel });

const targetLanguagePickSingle: QuickPickItem | undefined = await window.showQuickPick<QuickPickItem>(
altActions.concat(languageTagOptions),
Expand All @@ -203,7 +239,7 @@ async function selectNewTargetLanguages(fileType: string, multiSelectAllowed: bo
return undefined;
}
if (targetLanguagePickSingle.label === enterCustomLabel) {
var customTargetLanguage = await window.showInputBox({prompt: 'Enter target language tag', placeHolder: 'Example: en-US'});
var customTargetLanguage = await window.showInputBox({ prompt: 'Enter target language tag', placeHolder: 'Example: en-US' });
if (!customTargetLanguage) {
return undefined;
}
Expand Down Expand Up @@ -258,7 +294,7 @@ async function synchronizeTargetFile(sourceUri: Uri, targetUri: Uri | undefined,
const newFileContents = await XlfTranslator.synchronize(source, target, targetLanguage, workspaceFolder);

if (!newFileContents) {
throw new Error(`No ouput generated.`);
throw new Error(`No output generated.`);
}

targetUri = await FilesHelper.createNewTargetFile(targetUri, newFileContents, sourceUri, targetLanguage);
Expand All @@ -283,7 +319,7 @@ async function synchronizeAllFiles(sourceUri: Uri, targetUris: Uri[], workspaceF
equivalentLanguages = xliffWorkspaceConfiguration['equivalentLanguages'];
}
const equivalentLanguagesEnabled: boolean = xliffWorkspaceConfiguration['equivalentLanguagesEnabled'];
let slavesToMaster: {[id: string]: string} = {};
let slavesToMaster: { [id: string]: string } = {};
if (equivalentLanguagesEnabled) {
for (let master in equivalentLanguages) {
const slavePattern = equivalentLanguages[master];
Expand Down Expand Up @@ -362,7 +398,7 @@ async function synchronizeAllFiles(sourceUri: Uri, targetUris: Uri[], workspaceF
await autoRunTranslationChecks(workspaceFolder);
}

function getMasterLanguage(slavePatternsToMaster: {[id: string]: string}, targetLanguage: string): string | undefined {
function getMasterLanguage(slavePatternsToMaster: { [id: string]: string }, targetLanguage: string): string | undefined {
for (let slavePattern in slavePatternsToMaster) {
if (new RegExp(slavePattern).test(targetLanguage)) {
return slavePatternsToMaster[slavePattern];
Expand Down