提交 5440a2cd 编写于 作者: M Michel Kaporin

Merge branch 'original-nls' of https://github.com/michelkaporin/vscode into...

Merge branch 'original-nls' of https://github.com/michelkaporin/vscode into michelkaporin-original-nls
......@@ -13,7 +13,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { forEach } from 'vs/base/common/collections';
import { IExtensionPointUser, ExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
import { MenuId, MenuRegistry, ILocalizedString } from 'vs/platform/actions/common/actions';
namespace schema {
......@@ -128,8 +128,8 @@ namespace schema {
export interface IUserFriendlyCommand {
command: string;
title: string;
category?: string;
title: string | ILocalizedString;
category?: string | ILocalizedString;
icon?: IUserFriendlyIcon;
}
......@@ -144,12 +144,10 @@ namespace schema {
collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'command'));
return false;
}
if (isFalsyOrWhitespace(command.title)) {
collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'title'));
if (!isValidLocalizedString(command.title, collector, 'title')) {
return false;
}
if (command.category && typeof command.category !== 'string') {
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'category'));
if (command.category && !isValidLocalizedString(command.category, collector, 'category')) {
return false;
}
if (!isValidIcon(command.icon, collector)) {
......@@ -171,6 +169,21 @@ namespace schema {
return false;
}
function isValidLocalizedString(localized: string | ILocalizedString, collector: ExtensionMessageCollector, propertyName: string): boolean {
if (typeof localized === 'undefined') {
collector.error(localize('requireStringOrObject', "property `{0}` is mandatory and must be of type `string` or `object`", propertyName));
return false;
} else if (typeof localized === 'string' && isFalsyOrWhitespace(localized)) {
collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", propertyName));
return false;
} else if (typeof localized !== 'string' && (isFalsyOrWhitespace(localized.original) || isFalsyOrWhitespace(localized.value))) {
collector.error(localize('requirestrings', "properties `{0}` and `{1}` are mandatory and must be of type `string`", `${propertyName}.value`, `${propertyName}.original`));
return false;
}
return true;
}
const commandType: IJSONSchema = {
type: 'object',
properties: {
......
......@@ -14,11 +14,15 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
import { IDisposable } from 'vs/base/common/lifecycle';
import Event from 'vs/base/common/event';
export interface ILocalizedString {
value: string;
original: string;
}
export interface ICommandAction {
id: string;
title: string;
alias?: string;
category?: string;
title: string | ILocalizedString;
category?: string | ILocalizedString;
iconClass?: string;
}
......@@ -165,7 +169,7 @@ export class MenuItemAction extends ExecuteCommandAction {
arg: any,
@ICommandService commandService: ICommandService
) {
super(item.id, item.title, commandService);
typeof item.title === 'string' ? super(item.id, item.title, commandService) : super(item.id, item.title.value, commandService);
this._cssClass = item.iconClass;
this._enabled = true;
this._arg = arg;
......
......@@ -134,6 +134,8 @@ export class Menu implements IMenu {
}
// sort on titles
return a.command.title.localeCompare(b.command.title);
const aTitle = typeof a.command.title === 'string' ? a.command.title : a.command.title.value;
const bTitle = typeof b.command.title === 'string' ? b.command.title : b.command.title.value;
return aTitle.localeCompare(bTitle);
}
}
......@@ -112,40 +112,63 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
if (!exists) {
return extensionDescription;
}
return ExtensionManifestNLSReplacer.findMessageBundle(basename).then(messageBundle => {
if (!messageBundle) {
return ExtensionManifestNLSReplacer.findMessageBundles(basename).then((messageBundle) => {
if (!messageBundle.localized) {
return extensionDescription;
}
return pfs.readFile(messageBundle).then(messageBundleContent => {
return pfs.readFile(messageBundle.localized).then(messageBundleContent => {
let errors: json.ParseError[] = [];
let messages: { [key: string]: string; } = json.parse(messageBundleContent.toString(), errors);
if (errors.length > 0) {
errors.forEach((error) => {
this._collector.error(this._absoluteFolderPath, nls.localize('jsonParseFail', "Failed to parse {0}: {1}.", messageBundle, json.getParseErrorMessage(error.error)));
});
return ExtensionManifestNLSReplacer.resolveOriginalMessageBundle(messageBundle.original, errors).then(originalMessages => {
if (errors.length > 0) {
errors.forEach((error) => {
this._collector.error(this._absoluteFolderPath, nls.localize('jsonsParseFail', "Failed to parse {0} or {1}: {2}.", messageBundle.localized, messageBundle.original, json.getParseErrorMessage(error.error)));
});
return extensionDescription;
}
ExtensionManifestNLSReplacer._replaceNLStrings(extensionDescription, messages, originalMessages, this._collector, this._absoluteFolderPath);
return extensionDescription;
}
ExtensionManifestNLSReplacer._replaceNLStrings(extensionDescription, messages, this._collector, this._absoluteFolderPath);
return extensionDescription;
});
}, (err) => {
this._collector.error(this._absoluteFolderPath, nls.localize('fileReadFail', "Cannot read file {0}: {1}.", messageBundle, err.message));
this._collector.error(this._absoluteFolderPath, nls.localize('fileReadFail', "Cannot read file {0}: {1}.", messageBundle.localized, err.message));
return null;
});
});
});
}
private static findMessageBundle(basename: string): TPromise<string> {
return new TPromise<string>((c, e, p) => {
/**
* Parses original message bundle, returns null if the original message bundle is null.
*/
private static resolveOriginalMessageBundle(originalMessageBundle: string, errors: json.ParseError[]) {
return new TPromise<{ [key: string]: string; }>((c, e, p) => {
if (originalMessageBundle) {
pfs.readFile(originalMessageBundle).then(originalBundleContent => {
c(json.parse(originalBundleContent.toString(), errors));
});
} else {
c(null);
}
});
}
/**
* Finds localized message bundle and the original (unlocalized) one.
* If the localized file is not present, returns null for the original and marks original as localized.
*/
private static findMessageBundles(basename: string): TPromise<{ localized: string, original: string }> {
return new TPromise<{ localized: string, original: string }>((c, e, p) => {
function loop(basename: string, locale: string): void {
let toCheck = `${basename}.nls.${locale}.json`;
pfs.fileExists(toCheck).then(exists => {
if (exists) {
c(toCheck);
c({ localized: toCheck, original: `${basename}.nls.json` });
}
let index = locale.lastIndexOf('-');
if (index === -1) {
c(`${basename}.nls.json`);
c({ localized: `${basename}.nls.json`, original: null });
} else {
locale = locale.substring(0, index);
loop(basename, locale);
......@@ -154,18 +177,18 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
}
if (devMode || nlsConfig.pseudo || !nlsConfig.locale) {
return c(basename + '.nls.json');
return c({ localized: basename + '.nls.json', original: null });
}
loop(basename, nlsConfig.locale);
});
}
/**
* This routine make the following assumptions:
* The root element is a object literal
* This routine makes the following assumptions:
* The root element is an object literal
*/
private static _replaceNLStrings<T>(literal: T, messages: { [key: string]: string; }, collector: MessagesCollector, messageScope: string): void {
function processEntry(obj: any, key: string | number) {
private static _replaceNLStrings<T>(literal: T, messages: { [key: string]: string; }, originalMessages: { [key: string]: string }, collector: MessagesCollector, messageScope: string): void {
function processEntry(obj: any, key: string | number, command?: boolean) {
let value = obj[key];
if (Types.isString(value)) {
let str = <string>value;
......@@ -178,7 +201,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
// FF3B and FF3D is the Unicode zenkaku representation for [ and ]
message = '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D';
}
obj[key] = message;
obj[key] = command && (key === 'title' || key === 'category') && originalMessages ? { value: message, original: originalMessages[messageKey] } : message;
} else {
collector.warn(messageScope, nls.localize('missingNLSKey', "Couldn't find message for key {0}.", messageKey));
}
......@@ -186,12 +209,12 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
} else if (Types.isObject(value)) {
for (let k in value) {
if (value.hasOwnProperty(k)) {
processEntry(value, k);
k === 'commands' ? processEntry(value, k, true) : processEntry(value, k, command);
}
}
} else if (Types.isArray(value)) {
for (let i = 0; i < value.length; i++) {
processEntry(value, i);
processEntry(value, i, command);
}
}
}
......
......@@ -353,15 +353,27 @@ export class CommandsHandler extends QuickOpenHandler {
const entries: ActionCommandEntry[] = [];
for (let action of actions) {
const label = action.item.category
? nls.localize('cat.title', "{0}: {1}", action.item.category, action.item.title)
: action.item.title;
const title = typeof action.item.title === 'string' ? action.item.title : action.item.title.value;
let category, label = title;
if (action.item.category) {
category = typeof action.item.category === 'string' ? action.item.category : action.item.category.value;
label = nls.localize('cat.title', "{0}: {1}", category, title);
}
if (label) {
const labelHighlights = wordFilter(searchValue, label);
const keybinding = this.keybindingService.lookupKeybinding(action.item.id);
const keyLabel = keybinding ? keybinding.getLabel() : '';
const keyAriaLabel = keybinding ? keybinding.getAriaLabel() : '';
const alias = action.item.alias ? action.item.alias : null;
// Add an 'alias' in original language when running in different locale
const aliasTitle = (language !== LANGUAGE_DEFAULT && typeof action.item.title !== 'string') ? action.item.title.original : null;
const aliasCategory = (language !== LANGUAGE_DEFAULT && category && typeof action.item.category !== 'string') ? action.item.category.original : null;
let alias;
if (aliasTitle && category) {
alias = aliasCategory ? `${aliasCategory}: ${aliasTitle}` : `${category}: ${aliasTitle}`;
} else if (aliasTitle) {
alias = aliasTitle;
}
const aliasHighlights = alias ? wordFilter(searchValue, alias) : null;
if (labelHighlights || aliasHighlights) {
entries.push(this.instantiationService.createInstance(ActionCommandEntry, keyLabel, keyAriaLabel, label, alias, labelHighlights, aliasHighlights, action));
......
......@@ -1128,13 +1128,13 @@ class TaskService extends EventEmitter implements ITaskService {
let workbenchActionsRegistry = <IWorkbenchActionRegistry>Registry.as(WorkbenchActionExtensions.WorkbenchActions);
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureTaskRunnerAction, ConfigureTaskRunnerAction.ID, ConfigureTaskRunnerAction.TEXT), 'Tasks: Configure Task Runner', tasksCategory);
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureTaskRunnerAction, ConfigureTaskRunnerAction.ID, ConfigureTaskRunnerAction.TEXT), 'Configure Task Runner', tasksCategory);
MenuRegistry.addCommand({ id: 'workbench.action.tasks.showLog', title: nls.localize('ShowLogAction.label', "Show Task Log"), alias: 'Tasks: Show Task Log', category: tasksCategory });
MenuRegistry.addCommand({ id: 'workbench.action.tasks.runTask', title: nls.localize('RunTaskAction.label', "Run Task"), alias: 'Tasks: Run Task', category: tasksCategory });
MenuRegistry.addCommand({ id: 'workbench.action.tasks.terminate', title: nls.localize('TerminateAction.label', "Terminate Running Task"), alias: 'Tasks: Terminate Running Task', category: tasksCategory });
MenuRegistry.addCommand({ id: 'workbench.action.tasks.build', title: nls.localize('BuildAction.label', "Run Build Task"), alias: 'Tasks: Run Build Task', category: tasksCategory });
MenuRegistry.addCommand({ id: 'workbench.action.tasks.test', title: nls.localize('TestAction.label', "Run Test Task"), alias: 'Tasks: Run Test Task', category: tasksCategory });
MenuRegistry.addCommand({ id: 'workbench.action.tasks.showLog', title: { value: nls.localize('ShowLogAction.label', "Show Task Log"), original: 'Show Task Log' }, category: { value: tasksCategory, original: 'Tasks' } });
MenuRegistry.addCommand({ id: 'workbench.action.tasks.runTask', title: { value: nls.localize('RunTaskAction.label', "Run Task"), original: 'Run Task' }, category: { value: tasksCategory, original: 'Tasks' } });
MenuRegistry.addCommand({ id: 'workbench.action.tasks.terminate', title: { value: nls.localize('TerminateAction.label', "Terminate Running Task"), original: 'Terminate Running Task' }, category: { value: tasksCategory, original: 'Tasks' } });
MenuRegistry.addCommand({ id: 'workbench.action.tasks.build', title: { value: nls.localize('BuildAction.label', "Run Build Task"), original: 'Run Build Task' }, category: { value: tasksCategory, original: 'Tasks' } });
MenuRegistry.addCommand({ id: 'workbench.action.tasks.test', title: { value: nls.localize('TestAction.label', "Run Test Task"), original: 'Run Test Task' }, category: { value: tasksCategory, original: 'Tasks' } });
// MenuRegistry.addCommand( { id: 'workbench.action.tasks.rebuild', title: nls.localize('RebuildAction.label', 'Run Rebuild Task'), category: tasksCategory });
// MenuRegistry.addCommand( { id: 'workbench.action.tasks.clean', title: nls.localize('CleanAction.label', 'Run Clean Task'), category: tasksCategory });
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册