mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ADF-3093] Added style checking tool for en.json translation file (#3428)
* [ADF-3093] Started li18nt VS Code extension * [ADF-3093] Started work on UI style lint tool for VSCode * [ADF-3093] Added UI style rules up to sg0006 * [ADF-3093] Added remaining style rules * [ADF-3093] Added docs and command line tool * [ADF-3093] Removed Microsoft notices and updated licences to Apache-2.0
This commit is contained in:
committed by
Eugenio Romano
parent
4225bf5213
commit
dfc83489e2
66
tools/i18n/UIStyleRules.md
Normal file
66
tools/i18n/UIStyleRules.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# UI text style guide in rule form
|
||||
|
||||
This document lists the guidelines from the
|
||||
[Alfresco UI style guide](https://docs.alfresco.com/sites/docs.alfresco.com/files/public/docs_team/u2/Alfresco-Writing-Guide.pdf)
|
||||
in the form of separate rules. The list can be as a check to identify
|
||||
specific types of mistake and suggest solutions. However, the main
|
||||
[style guide](https://docs.alfresco.com/sites/docs.alfresco.com/files/public/docs_team/u2/Alfresco-Writing-Guide.pdf)
|
||||
remains the definitive source for the guidelines and explains and develops the reasons for
|
||||
style choices in greater depth.
|
||||
|
||||
## Rules
|
||||
|
||||
### SG0001: Avoid polite words ("please", "thank you")
|
||||
|
||||
People tend to find direct commands ("do this", "go here") rude in everyday conversation but, in UI text, the meaning is very different.
|
||||
Saying "use xxx to solve problem yyy" reads as a recommendation
|
||||
("if you think you have problem yyy then using xxx is a good way to solve it").
|
||||
Using expressions like "please", "thank you", and "feel free" takes up space and makes the text less direct and easy to read.
|
||||
|
||||
### SG0002: Avoid interjections ("oops", "wow", "yeah")
|
||||
|
||||
It is quite common for UI text in other products to include words like this to add a bit of fun to the UI.
|
||||
However, it is Alfresco's style to avoid these words to keep the UI more "serious" and professional.
|
||||
|
||||
### SG0003: Avoid exclamations ("!")
|
||||
|
||||
Exclamations generally look as though they are supposed to be funny or angry. This doesn't
|
||||
fit in well with Alfresco's professional UI style.
|
||||
|
||||
### SG0004: Avoid unusual punctuation (";", "~", "^")
|
||||
|
||||
These punctuation marks are valid in English but their meaning is often subtle and they
|
||||
are rarely used. The text is generally clearer without them, especially for a user who isn't
|
||||
a native English speaker.
|
||||
|
||||
### SG0005: Don't use the ampersand ("&") as a replacement for "and"
|
||||
|
||||
The ampersand ("&") character is common in signs and captions but isn't normally used in
|
||||
messages and body text. Use the word "and" in full.
|
||||
|
||||
### SG0006: Write numbers using digits instead of words
|
||||
|
||||
Numbers take up a lot more screen space when they are written out as words ("one hundred
|
||||
and twenty three") rather than digits ("123"). Style guides differ on this issue but Alfresco's
|
||||
style is to prefer digits over words when writing numbers. The exception is for numbers from
|
||||
1 million upwards. For these, write digits followed by "million", "billion", etc. Something like
|
||||
"10 billion" is easier to read than 10000000000.
|
||||
|
||||
### SG0007: Contractions ("can't", "won't" "isn't") are usually better than the equivalent phrase
|
||||
|
||||
Contractions such as "can't", "won't" and "isn't" are shorter and more familiar than "cannot",
|
||||
"will not", "is not" and help to make the UI seem a bit friendlier. Avoid the full phrase except
|
||||
in cases where you want to emphasise an instruction very strongly ("Do not change these settings
|
||||
unless you are sure you need to do so").
|
||||
|
||||
### SG0008: Leave out trademark and copyright symbols
|
||||
|
||||
There is no legal requirement to use trademark and copyright symbols ("™", "©", "®") in UI
|
||||
text. They will be covered in the docs if they are needed at all, so don't use them in the UI.
|
||||
|
||||
### SG0009: Avoid abbreviations for common phrases ("etc", "e.g.")
|
||||
|
||||
Abbreviations that represent English phrases ("i.e.", "etc") tend to read awkwardly and
|
||||
so they should be avoided in UI text. The exception is OK, which is widely used.
|
||||
However, abbreviations and acronyms for well-known products and terms (ADF, SDK)
|
||||
are fine.
|
25
tools/i18n/cmdLi18nt.js
Normal file
25
tools/i18n/cmdLi18nt.js
Normal file
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
var program = require("commander");
|
||||
var SGStyleRules_1 = require("./li18nt/server/src/SGStyleRules");
|
||||
program
|
||||
.usage(" <source>")
|
||||
.parse(process.argv);
|
||||
if (program.args.length === 0) {
|
||||
console.log('Error: source file "en.json" must be provided');
|
||||
process.exit();
|
||||
}
|
||||
var text = fs.readFileSync(path.resolve(program.args[0]), 'utf8');
|
||||
var lines = text.split(/\r?\n/g);
|
||||
var messages = [];
|
||||
lines.forEach(function (line, index) {
|
||||
SGStyleRules_1.rules.forEach(function (rule) {
|
||||
var newProblems = rule(line, index);
|
||||
messages.push.apply(messages, newProblems);
|
||||
});
|
||||
});
|
||||
messages.forEach(function (message) {
|
||||
console.log("Line " + message.lineNum + " (" + message.startCharPos + "-" + message.endCharPos + "): " + SGStyleRules_1.sgErrorMessages[message.messageCode]);
|
||||
});
|
33
tools/i18n/cmdLi18nt.ts
Normal file
33
tools/i18n/cmdLi18nt.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as program from 'commander';
|
||||
|
||||
import {
|
||||
SGStringProblem, sgErrorMessages, rules
|
||||
} from './li18nt/server/src/SGStyleRules';
|
||||
|
||||
|
||||
program
|
||||
.usage(" <source>")
|
||||
.parse(process.argv);
|
||||
|
||||
if (program.args.length === 0) {
|
||||
console.log('Error: source file "en.json" must be provided');
|
||||
process.exit();
|
||||
}
|
||||
|
||||
let text = fs.readFileSync(path.resolve(program.args[0]), 'utf8');
|
||||
|
||||
let lines = text.split(/\r?\n/g);
|
||||
let messages: SGStringProblem[] = [];
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
rules.forEach(rule => {
|
||||
let newProblems = rule(line, index);
|
||||
messages.push(...newProblems);
|
||||
});
|
||||
});
|
||||
|
||||
messages.forEach(message => {
|
||||
console.log(`Line ${message.lineNum} (${message.startCharPos}-${message.endCharPos}): ${sgErrorMessages[message.messageCode]}`)
|
||||
});
|
3
tools/i18n/li18nt/.gitignore
vendored
Normal file
3
tools/i18n/li18nt/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
out
|
||||
node_modules
|
||||
client/server
|
32
tools/i18n/li18nt/.vscode/launch.json
vendored
Normal file
32
tools/i18n/li18nt/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
// List of configurations. Add new configurations or edit existing ones.
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Client",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceRoot}/client"
|
||||
],
|
||||
"stopOnEntry": false,
|
||||
"sourceMaps": true,
|
||||
"outFiles": [
|
||||
"${workspaceRoot}/client/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "watch:client"
|
||||
},
|
||||
{
|
||||
"name": "Attach to Server",
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"port": 6009,
|
||||
"sourceMaps": true,
|
||||
"outFiles": [
|
||||
"${workspaceRoot}/client/server/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "watch:server"
|
||||
}
|
||||
]
|
||||
}
|
15
tools/i18n/li18nt/.vscode/settings.json
vendored
Normal file
15
tools/i18n/li18nt/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
// set to false to hide directory from the VS Code Document Sidebar
|
||||
"files.exclude": {
|
||||
"client/out/**/*": false,
|
||||
"client/server/**/*": false,
|
||||
"client/node_modules": false,
|
||||
"server/node_modules": false
|
||||
},
|
||||
// set this to false to include "out" folder in search results
|
||||
"search.exclude": {
|
||||
"out": true
|
||||
},
|
||||
"typescript.tsdk": "./node_modules/typescript/lib",
|
||||
"typescript.tsc.autoDetect": "off"
|
||||
}
|
79
tools/i18n/li18nt/.vscode/tasks.json
vendored
Normal file
79
tools/i18n/li18nt/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "compile",
|
||||
"dependsOn": [
|
||||
"compile:client",
|
||||
"compile:server"
|
||||
],
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "compile:client",
|
||||
"type": "npm",
|
||||
"script": "compile:client",
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"panel": "dedicated",
|
||||
"reveal": "never"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$tsc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "compile:server",
|
||||
"type": "npm",
|
||||
"script": "compile:server",
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"panel": "dedicated",
|
||||
"reveal": "never"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$tsc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"dependsOn": [
|
||||
"watch:client",
|
||||
"watch:server"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "watch:client",
|
||||
"type": "npm",
|
||||
"script": "watch:client",
|
||||
"isBackground": true,
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"panel": "dedicated",
|
||||
"reveal": "never"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$tsc-watch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "watch:server",
|
||||
"type": "npm",
|
||||
"script": "watch:server",
|
||||
"isBackground": true,
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"panel": "dedicated",
|
||||
"reveal": "never"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$tsc-watch"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
36
tools/i18n/li18nt/README.md
Normal file
36
tools/i18n/li18nt/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# li18nt UI text style checker
|
||||
|
||||
The purpose of this tool is to provide style "lint" checking for the en.json
|
||||
file, which is used as the starting point for i18n in the user interface. The
|
||||
definitive guidelines can be found in the main
|
||||
[style guide](https://docs.alfresco.com/sites/docs.alfresco.com/files/public/docs_team/u2/Alfresco-Writing-Guide.pdf) document. This tool implements the guidelines as a set of rules that can be
|
||||
checked automatically for common errors (the [style rules](../UIStyleRules.md) file
|
||||
contains a full description of the rules currently in use).
|
||||
|
||||
## Installing and using the VSCode extension
|
||||
|
||||
The VS Code extension shows the style warnings as underlined sections in the text
|
||||
with corresponding notes in the Problems window, a lot like standard programming
|
||||
language errors.
|
||||
|
||||
The extension is not available in the VS Code marketplace but you can install it
|
||||
locally by copying it to the extensions folder. This can be found at the path
|
||||
|
||||
`$HOME/.vscode/extensions`
|
||||
|
||||
...on MacOSX and at
|
||||
|
||||
`%USERPROFILE%\.vscode\extensions`
|
||||
|
||||
...on Windows. Copy the `client` folder (from `alfresco-ng2-components/tools/i18n/li18nt/`)
|
||||
to the extensions folder and rename it to `li18nt`. If there is no `node_modules`
|
||||
folder in the new `li18nt` folder then you should also `cd` into this folder and
|
||||
run `npm install`. When you restart VS Code, you should find `li18nt` listed among
|
||||
the installed extensions in the extensions panel.
|
||||
|
||||
When active, the extension will only check the text of files named `en.json`.
|
||||
Double-click an item from the Problems window in VS Code to highlight the section
|
||||
of text where the issue occurs. You can find out more about why the error has
|
||||
occurred and what to do about it in the [style rules](../UIStyleRules.md) file.
|
||||
For the full description and explanation of all style guidelines, see the main
|
||||
[style guide](https://docs.alfresco.com/sites/docs.alfresco.com/files/public/docs_team/u2/Alfresco-Writing-Guide.pdf).
|
9
tools/i18n/li18nt/client/.vscodeignore
Normal file
9
tools/i18n/li18nt/client/.vscodeignore
Normal file
@@ -0,0 +1,9 @@
|
||||
.vscode/**
|
||||
typings/**
|
||||
out/test/**
|
||||
test/**
|
||||
src/**
|
||||
**/*.map
|
||||
.gitignore
|
||||
tsconfig.json
|
||||
vsc-extension-quickstart.md
|
1795
tools/i18n/li18nt/client/package-lock.json
generated
Normal file
1795
tools/i18n/li18nt/client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
58
tools/i18n/li18nt/client/package.json
Normal file
58
tools/i18n/li18nt/client/package.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"name": "li18nt",
|
||||
"description": "Style checker for ADF translation files.",
|
||||
"author": "Alfresco Software",
|
||||
"license": "Apache-2.0",
|
||||
"version": "0.0.1",
|
||||
"publisher": "vscode",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Alfresco/alfresco-ng2-components"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.22.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onLanguage:json"
|
||||
],
|
||||
"main": "./out/src/extension",
|
||||
"contributes": {
|
||||
"configuration": {
|
||||
"type": "object",
|
||||
"title": "Main configuration",
|
||||
"properties": {
|
||||
"li18nt.maxNumberOfProblems": {
|
||||
"scope": "resource",
|
||||
"type": "number",
|
||||
"default": 100,
|
||||
"description": "Controls the maximum number of problems produced by the server."
|
||||
},
|
||||
"li18nt.trace.server": {
|
||||
"scope": "window",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"off",
|
||||
"messages",
|
||||
"verbose"
|
||||
],
|
||||
"default": "off",
|
||||
"description": "Traces the communication between VSCode and the language server."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "tsc -p ./",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -w -p ./",
|
||||
"update-vscode": "node ./node_modules/vscode/bin/install",
|
||||
"postinstall": "node ./node_modules/vscode/bin/install"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode": "^1.1.17",
|
||||
"vscode-languageclient": "^4.1.3"
|
||||
}
|
||||
}
|
40
tools/i18n/li18nt/client/src/extension.ts
Normal file
40
tools/i18n/li18nt/client/src/extension.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
import * as path from 'path';
|
||||
|
||||
import { workspace, ExtensionContext } from 'vscode';
|
||||
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient';
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
|
||||
// The server is implemented in node
|
||||
let serverModule = context.asAbsolutePath(path.join('server', 'server.js'));
|
||||
// The debug options for the server
|
||||
let debugOptions = { execArgv: ["--nolazy", "--inspect=6009"] };
|
||||
|
||||
// If the extension is launched in debug mode then the debug server options are used
|
||||
// Otherwise the run options are used
|
||||
let serverOptions: ServerOptions = {
|
||||
run : { module: serverModule, transport: TransportKind.ipc },
|
||||
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
|
||||
}
|
||||
|
||||
// Options to control the language client
|
||||
let clientOptions: LanguageClientOptions = {
|
||||
// Register the server for plain text documents
|
||||
documentSelector: [{scheme: 'file', language: 'json'}],
|
||||
synchronize: {
|
||||
// Synchronize the setting section 'languageServerExample' to the server
|
||||
configurationSection: 'li18nt',
|
||||
// Notify the server about file changes to '.clientrc files contain in the workspace
|
||||
fileEvents: workspace.createFileSystemWatcher('**/.clientrc')
|
||||
}
|
||||
}
|
||||
|
||||
// Create the language client and start the client.
|
||||
let disposable = new LanguageClient('li18nt', 'li18nt', serverOptions, clientOptions).start();
|
||||
|
||||
// Push the disposable to the context's subscriptions so that the
|
||||
// client can be deactivated on extension deactivation
|
||||
context.subscriptions.push(disposable);
|
||||
}
|
19
tools/i18n/li18nt/client/tsconfig.json
Normal file
19
tools/i18n/li18nt/client/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"rootDir": ".",
|
||||
"outDir": "out",
|
||||
"lib": [ "es2016" ],
|
||||
"sourceMap": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"server"
|
||||
]
|
||||
}
|
5
tools/i18n/li18nt/package-lock.json
generated
Normal file
5
tools/i18n/li18nt/package-lock.json
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "lsp--sample",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 1
|
||||
}
|
21
tools/i18n/li18nt/package.json
Normal file
21
tools/i18n/li18nt/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "li18nt",
|
||||
"description": "Style checker for ADF translation files",
|
||||
"author": "Alfresco Software",
|
||||
"license": "Apache-2.0",
|
||||
"version": "0.0.1",
|
||||
"publisher": "vscode",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Alfresco/alfresco-ng2-components"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "cd server && npm install && cd ../client && npm install && cd ..",
|
||||
"compile": "tsc -p client/tsconfig.json && cd server && npm run installServer && cd .. && tsc -p server/tsconfig.json",
|
||||
"compile:client": "tsc -p client/tsconfig.json",
|
||||
"watch:client": "tsc -w -p client/tsconfig.json",
|
||||
"compile:server": "cd server && npm run installServer && cd .. && tsc -p server/tsconfig.json",
|
||||
"watch:server": "cd server && npm run installServer && cd .. && tsc -w -p server/tsconfig.json"
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
41
tools/i18n/li18nt/server/package-lock.json
generated
Normal file
41
tools/i18n/li18nt/server/package-lock.json
generated
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "lsp-sample",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"vscode-jsonrpc": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz",
|
||||
"integrity": "sha512-T24Jb5V48e4VgYliUXMnZ379ItbrXgOimweKaJshD84z+8q7ZOZjJan0MeDe+Ugb+uqERDVV8SBmemaGMSMugA=="
|
||||
},
|
||||
"vscode-languageserver": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-4.1.3.tgz",
|
||||
"integrity": "sha512-D6p3q9x8QPtPLRUO5d2UKizjFYfg8zLVJqKoMpAaom8Wuhl1oKRCjeLg+Cp4mgPeCwR71wbgX2BM/jL51ni/0g==",
|
||||
"requires": {
|
||||
"vscode-languageserver-protocol": "3.7.2",
|
||||
"vscode-uri": "1.0.3"
|
||||
}
|
||||
},
|
||||
"vscode-languageserver-protocol": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.7.2.tgz",
|
||||
"integrity": "sha512-VVJwIA/FPl/FnVtrns0FPK6TLi/ET7n1Yo6tCrm6aG7+yAVwIGWdpTmKE+nbP8wEMMbHCkIabk63IJvfz2HNRg==",
|
||||
"requires": {
|
||||
"vscode-jsonrpc": "3.6.2",
|
||||
"vscode-languageserver-types": "3.7.2"
|
||||
}
|
||||
},
|
||||
"vscode-languageserver-types": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.7.2.tgz",
|
||||
"integrity": "sha512-L9D2RA+PDS2CiyhLQY5ZrOmyRvXyjc4Ha8s9PqS6mIgGxj00R5Xx2vLKBnAOVfrawJXYZST+2hioMks6SQVU7A=="
|
||||
},
|
||||
"vscode-uri": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.3.tgz",
|
||||
"integrity": "sha1-Yxvb9xbcyrDmUpGo3CXCMjIIWlI="
|
||||
}
|
||||
}
|
||||
}
|
22
tools/i18n/li18nt/server/package.json
Normal file
22
tools/i18n/li18nt/server/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "li18nt",
|
||||
"description": "Style checker for ADF translation files.",
|
||||
"version": "0.0.1",
|
||||
"author": "Alfresco Software",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Alfresco/alfresco-ng2-components"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-languageserver": "^4.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"installServer": "installServerIntoExtension ../client ./package.json ./tsconfig.json",
|
||||
"compile": "installServerIntoExtension ../client ./package.json ./tsconfig.json && tsc -p .",
|
||||
"watch": "installServerIntoExtension ../client ./package.json ./tsconfig.json && tsc -w -p ."
|
||||
}
|
||||
}
|
73
tools/i18n/li18nt/server/src/SGStyleRules.js
Normal file
73
tools/i18n/li18nt/server/src/SGStyleRules.js
Normal file
@@ -0,0 +1,73 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var SGStringProblem = /** @class */ (function () {
|
||||
function SGStringProblem(messageCode, lineNum, startCharPos, endCharPos) {
|
||||
this.messageCode = messageCode;
|
||||
this.lineNum = lineNum;
|
||||
this.startCharPos = startCharPos;
|
||||
this.endCharPos = endCharPos;
|
||||
}
|
||||
return SGStringProblem;
|
||||
}());
|
||||
exports.SGStringProblem = SGStringProblem;
|
||||
exports.sgErrorMessages = {
|
||||
'SG0001': 'Polite words are not recommended for UI text',
|
||||
'SG0002': 'Avoid using interjections in UI text ("oops", "yeah", "wow")',
|
||||
'SG0003': 'Avoid exclamations ("!")',
|
||||
'SG0004': 'Avoid unusual punctuation marks (";", "~", "^")',
|
||||
'SG0005': 'Don\'t use the ampersand ("&") as a replacement for "and"',
|
||||
'SG0006': 'Write numbers using digits instead of words',
|
||||
'SG0007': 'Contractions ("can\'t", "won\'t" "isn\'t") are usually better than the equivalent phrase',
|
||||
'SG0008': 'Leave out trademark and copyright symbols',
|
||||
'SG0009': 'Avoid abbreviations for common phrases ("etc", "e.g.")'
|
||||
};
|
||||
exports.rules = [
|
||||
sg0001, sg0002, sg0003, sg0004, sg0005, sg0006, sg0007, sg0008, sg0009
|
||||
];
|
||||
function sg0001(line, lineNum) {
|
||||
// Use global regex to allow repeated searching from the end of the
|
||||
// previous match.
|
||||
var checkWords = /\b(please|thanks|thank you|sorry)\b/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0001");
|
||||
}
|
||||
function sg0002(line, lineNum) {
|
||||
var checkWords = /\b(whoops|oops|wow|yeah|hey|oh|aw)\b/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0002");
|
||||
}
|
||||
function sg0003(line, lineNum) {
|
||||
var checkWords = /!+/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0003");
|
||||
}
|
||||
function sg0004(line, lineNum) {
|
||||
var checkWords = /[;~\^]+/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0004");
|
||||
}
|
||||
function sg0005(line, lineNum) {
|
||||
var checkWords = /&+/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0005");
|
||||
}
|
||||
function sg0006(line, lineNum) {
|
||||
var checkWords = /\b(two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty|thirty|forty|fifty|sixty|seventy|eighty|ninety|hundred|thousand)\b/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0006");
|
||||
}
|
||||
function sg0007(line, lineNum) {
|
||||
var checkWords = /\b(cannot|do not|will not|have not|is not|should not|you are|we will)\b/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0007");
|
||||
}
|
||||
function sg0008(line, lineNum) {
|
||||
var checkWords = /[\u00a9\u00ae\u2122]+/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0008");
|
||||
}
|
||||
function sg0009(line, lineNum) {
|
||||
var checkWords = /\b(etc|e\.g\.|eg|i\.e\.|ie|n\.b\.|nb)\b/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0009");
|
||||
}
|
||||
function checkForRegExpMatches(re, line, lineNum, ruleName) {
|
||||
var problems = [];
|
||||
var matchInfo = re.exec(line);
|
||||
while (matchInfo) {
|
||||
problems.push(new SGStringProblem(ruleName, lineNum, matchInfo.index, matchInfo.index + matchInfo[0].length));
|
||||
matchInfo = re.exec(line);
|
||||
}
|
||||
return problems;
|
||||
}
|
108
tools/i18n/li18nt/server/src/SGStyleRules.ts
Normal file
108
tools/i18n/li18nt/server/src/SGStyleRules.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
|
||||
export class SGStringProblem {
|
||||
constructor(
|
||||
public messageCode: string,
|
||||
public lineNum: number,
|
||||
public startCharPos: number,
|
||||
public endCharPos: number
|
||||
){}
|
||||
}
|
||||
|
||||
|
||||
export let sgErrorMessages: {[index: string]: string} = {
|
||||
'SG0001': 'Polite words are not recommended for UI text',
|
||||
'SG0002': 'Avoid using interjections in UI text ("oops", "yeah", "wow")',
|
||||
'SG0003': 'Avoid exclamations ("!")',
|
||||
'SG0004': 'Avoid unusual punctuation marks (";", "~", "^")',
|
||||
'SG0005': 'Don\'t use the ampersand ("&") as a replacement for "and"',
|
||||
'SG0006': 'Write numbers using digits instead of words',
|
||||
'SG0007': 'Contractions ("can\'t", "won\'t" "isn\'t") are usually better than the equivalent phrase',
|
||||
'SG0008': 'Leave out trademark and copyright symbols',
|
||||
'SG0009': 'Avoid abbreviations for common phrases ("etc", "e.g.")'
|
||||
};
|
||||
|
||||
|
||||
export type SGRule = (line: string, lineNum: number) => SGStringProblem[];
|
||||
|
||||
|
||||
export let rules: SGRule[] = [
|
||||
sg0001, sg0002, sg0003, sg0004, sg0005, sg0006, sg0007, sg0008, sg0009
|
||||
];
|
||||
|
||||
|
||||
function sg0001(line: string, lineNum: number): SGStringProblem[] {
|
||||
// Use global regex to allow repeated searching from the end of the
|
||||
// previous match.
|
||||
let checkWords = /\b(please|thanks|thank you|sorry)\b/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0001");
|
||||
}
|
||||
|
||||
|
||||
function sg0002(line: string, lineNum: number): SGStringProblem[] {
|
||||
let checkWords = /\b(whoops|oops|wow|yeah|hey|oh|aw)\b/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0002");
|
||||
}
|
||||
|
||||
|
||||
function sg0003(line: string, lineNum: number): SGStringProblem[] {
|
||||
let checkWords = /!+/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0003");
|
||||
}
|
||||
|
||||
|
||||
function sg0004(line: string, lineNum: number): SGStringProblem[] {
|
||||
let checkWords = /[;~\^]+/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0004");
|
||||
}
|
||||
|
||||
|
||||
function sg0005(line: string, lineNum: number): SGStringProblem[] {
|
||||
let checkWords = /&+/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0005");
|
||||
}
|
||||
|
||||
|
||||
function sg0006(line: string, lineNum: number): SGStringProblem[] {
|
||||
let checkWords = /\b(two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty|thirty|forty|fifty|sixty|seventy|eighty|ninety|hundred|thousand)\b/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0006");
|
||||
}
|
||||
|
||||
|
||||
function sg0007(line: string, lineNum: number): SGStringProblem[] {
|
||||
let checkWords = /\b(cannot|do not|will not|have not|is not|should not|you are|we will)\b/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0007");
|
||||
}
|
||||
|
||||
|
||||
function sg0008(line: string, lineNum: number): SGStringProblem[] {
|
||||
let checkWords = /[\u00a9\u00ae\u2122]+/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0008");
|
||||
}
|
||||
|
||||
|
||||
function sg0009(line: string, lineNum: number): SGStringProblem[] {
|
||||
let checkWords = /\b(etc|e\.g\.|eg|i\.e\.|ie|n\.b\.|nb)\b/gi;
|
||||
return checkForRegExpMatches(checkWords, line, lineNum, "SG0009");
|
||||
}
|
||||
|
||||
|
||||
function checkForRegExpMatches(re: RegExp, line: string, lineNum: number, ruleName: string): SGStringProblem[] {
|
||||
let problems: SGStringProblem[] = [];
|
||||
|
||||
let matchInfo = re.exec(line);
|
||||
|
||||
while (matchInfo) {
|
||||
problems.push(
|
||||
new SGStringProblem(
|
||||
ruleName,
|
||||
lineNum,
|
||||
matchInfo.index,
|
||||
matchInfo.index + matchInfo[0].length
|
||||
)
|
||||
);
|
||||
|
||||
matchInfo = re.exec(line);
|
||||
}
|
||||
|
||||
return problems;
|
||||
}
|
170
tools/i18n/li18nt/server/src/server.ts
Normal file
170
tools/i18n/li18nt/server/src/server.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
'use strict';
|
||||
|
||||
import {
|
||||
IPCMessageReader, IPCMessageWriter, createConnection, IConnection, TextDocuments, TextDocument,
|
||||
Diagnostic, DiagnosticSeverity, InitializeResult, TextDocumentPositionParams, CompletionItem,
|
||||
CompletionItemKind
|
||||
} from 'vscode-languageserver';
|
||||
|
||||
import {
|
||||
SGStringProblem, sgErrorMessages, rules
|
||||
} from './SGStyleRules';
|
||||
|
||||
// Create a connection for the server. The connection uses Node's IPC as a transport
|
||||
let connection: IConnection = createConnection(new IPCMessageReader(process), new IPCMessageWriter(process));
|
||||
|
||||
// Create a simple text document manager. The text document manager
|
||||
// supports full document sync only
|
||||
let documents: TextDocuments = new TextDocuments();
|
||||
// Make the text document manager listen on the connection
|
||||
// for open, change and close text document events
|
||||
documents.listen(connection);
|
||||
|
||||
|
||||
//let shouldSendDiagnosticRelatedInformation: boolean = false;
|
||||
|
||||
// After the server has started the client sends an initialize request. The server receives
|
||||
// in the passed params the rootPath of the workspace plus the client capabilities.
|
||||
connection.onInitialize((_params): InitializeResult => {
|
||||
//shouldSendDiagnosticRelatedInformation = _params.capabilities && _params.capabilities.textDocument && _params.capabilities.textDocument.publishDiagnostics && _params.capabilities.textDocument.publishDiagnostics.relatedInformation;
|
||||
return {
|
||||
capabilities: {
|
||||
// Tell the client that the server works in FULL text document sync mode
|
||||
textDocumentSync: documents.syncKind,
|
||||
// Tell the client that the server support code complete
|
||||
completionProvider: {
|
||||
resolveProvider: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// The content of a text document has changed. This event is emitted
|
||||
// when the text document first opened or when its content has changed.
|
||||
documents.onDidChangeContent((change) => {
|
||||
if (change.document.uri.endsWith('en.json')) {
|
||||
validateTextDocument(change.document);
|
||||
}
|
||||
});
|
||||
|
||||
// The settings interface describe the server relevant settings part
|
||||
interface Settings {
|
||||
li18nt: ExampleSettings;
|
||||
}
|
||||
|
||||
// These are the example settings we defined in the client's package.json
|
||||
// file
|
||||
interface ExampleSettings {
|
||||
maxNumberOfProblems: number;
|
||||
}
|
||||
|
||||
// hold the maxNumberOfProblems setting
|
||||
let maxNumberOfProblems: number;
|
||||
// The settings have changed. Is send on server activation
|
||||
// as well.
|
||||
connection.onDidChangeConfiguration((change) => {
|
||||
let settings = <Settings>change.settings;
|
||||
maxNumberOfProblems = settings.li18nt.maxNumberOfProblems || 100;
|
||||
// Revalidate any open text documents
|
||||
documents.all().forEach(validateTextDocument);
|
||||
});
|
||||
|
||||
|
||||
|
||||
function validateTextDocument(textDocument: TextDocument): void {
|
||||
let diagnostics: Diagnostic[] = [];
|
||||
let messages: SGStringProblem[] = [];
|
||||
|
||||
let lines = textDocument.getText().split(/\r?\n/g);
|
||||
let problems = 0;
|
||||
|
||||
for (var i = 0; i < lines.length && problems < maxNumberOfProblems; i++) {
|
||||
let line = lines[i];
|
||||
|
||||
rules.forEach(rule => {
|
||||
let newProblems = rule(line, i);
|
||||
messages.push(...newProblems);
|
||||
problems += newProblems.length;
|
||||
});
|
||||
}
|
||||
|
||||
messages.forEach(message => {
|
||||
let errorMessage = sgErrorMessages[message.messageCode] || "No message available";
|
||||
let fullMessageText = `${message.messageCode}: ${errorMessage}`;
|
||||
let diag: Diagnostic = {
|
||||
severity: DiagnosticSeverity.Warning,
|
||||
range: {
|
||||
start: { line: message.lineNum, character: message.startCharPos },
|
||||
end: { line: message.lineNum, character: message.endCharPos }
|
||||
},
|
||||
message: fullMessageText,
|
||||
source: 'li18nt'
|
||||
}
|
||||
|
||||
diagnostics.push(diag);
|
||||
});
|
||||
|
||||
// Send the computed diagnostics to VSCode.
|
||||
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
|
||||
}
|
||||
|
||||
connection.onDidChangeWatchedFiles((_change) => {
|
||||
// Monitored files have change in VSCode
|
||||
connection.console.log('We received an file change event');
|
||||
});
|
||||
|
||||
|
||||
// This handler provides the initial list of the completion items.
|
||||
connection.onCompletion((_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {
|
||||
// The pass parameter contains the position of the text document in
|
||||
// which code complete got requested. For the example we ignore this
|
||||
// info and always provide the same completion items.
|
||||
return [
|
||||
{
|
||||
label: 'TypeScript',
|
||||
kind: CompletionItemKind.Text,
|
||||
data: 1
|
||||
},
|
||||
{
|
||||
label: 'JavaScript',
|
||||
kind: CompletionItemKind.Text,
|
||||
data: 2
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// This handler resolve additional information for the item selected in
|
||||
// the completion list.
|
||||
connection.onCompletionResolve((item: CompletionItem): CompletionItem => {
|
||||
if (item.data === 1) {
|
||||
item.detail = 'TypeScript details',
|
||||
item.documentation = 'TypeScript documentation'
|
||||
} else if (item.data === 2) {
|
||||
item.detail = 'JavaScript details',
|
||||
item.documentation = 'JavaScript documentation'
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
/*
|
||||
connection.onDidOpenTextDocument((params) => {
|
||||
// A text document got opened in VSCode.
|
||||
// params.uri uniquely identifies the document. For documents store on disk this is a file URI.
|
||||
// params.text the initial full content of the document.
|
||||
connection.console.log(`${params.textDocument.uri} opened.`);
|
||||
});
|
||||
connection.onDidChangeTextDocument((params) => {
|
||||
// The content of a text document did change in VSCode.
|
||||
// params.uri uniquely identifies the document.
|
||||
// params.contentChanges describe the content changes to the document.
|
||||
connection.console.log(`${params.textDocument.uri} changed: ${JSON.stringify(params.contentChanges)}`);
|
||||
});
|
||||
connection.onDidCloseTextDocument((params) => {
|
||||
// A text document got closed in VSCode.
|
||||
// params.uri uniquely identifies the document.
|
||||
connection.console.log(`${params.textDocument.uri} closed.`);
|
||||
});
|
||||
*/
|
||||
|
||||
// Listen on the connection
|
||||
connection.listen();
|
17
tools/i18n/li18nt/server/tsconfig.json
Normal file
17
tools/i18n/li18nt/server/tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"lib" : [ "es2016" ],
|
||||
"outDir": "../client/server"
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
Reference in New Issue
Block a user