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

Localization service override #385

Merged
merged 8 commits into from
Apr 9, 2024
Merged
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ Additionally, several packages that include the VSCode version of some services
- Survey/feedback support
- **Update**
- Update detection, release notes...
- **Localization**
- Register callbacks to update the display language from the VSCode UI (either from the `Set Display Language` command or from the extension gallery extension packs)

Usage:

Expand Down
23 changes: 22 additions & 1 deletion demo/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@
"@codingame/monaco-vscode-workspace-trust-service-override": "file:../dist/service-override-workspace-trust",
"@codingame/monaco-vscode-xml-default-extension": "file:../dist/default-extension-xml",
"@codingame/monaco-vscode-yaml-default-extension": "file:../dist/default-extension-yaml",
"@codingame/monaco-vscode-localization-service-override": "file:../dist/service-override-localization",
"ansi-colors": "^4.1.3",
"dockerode": "^4.0.2",
"express": "^4.19.2",
Expand Down
13 changes: 0 additions & 13 deletions demo/src/main.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,6 @@ void getApi().then(async vscode => {
code: 42
}])

const locale = new URLSearchParams(window.location.search).get('locale') ?? ''
const select: HTMLSelectElement = document.querySelector('#localeSelect')!
select.value = locale
select.addEventListener('change', () => {
const url = new URL(window.location.href)
if (select.value !== '') {
url.searchParams.set('locale', select.value)
} else {
url.searchParams.delete('locale')
}
window.location.href = url.toString()
})

document.querySelector('#toggleFullWorkbench')!.addEventListener('click', async () => {
const url = new URL(window.location.href)
if (url.searchParams.get('mode') === 'full-workbench') {
Expand Down
66 changes: 65 additions & 1 deletion demo/src/setup.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import getSpeechServiceOverride from '@codingame/monaco-vscode-speech-service-ov
import getSurveyServiceOverride from '@codingame/monaco-vscode-survey-service-override'
import getUpdateServiceOverride from '@codingame/monaco-vscode-update-service-override'
import getExplorerServiceOverride from '@codingame/monaco-vscode-explorer-service-override'
import getLocalizationServiceOverride from '@codingame/monaco-vscode-localization-service-override'
import { EnvironmentOverride } from 'vscode/workbench'
import { Worker } from './tools/crossOriginWorker'
import defaultKeybindings from './user/keybindings.json?raw'
Expand Down Expand Up @@ -248,6 +249,8 @@ export const constructOptions: IWorkbenchConstructionOptions = {
message: 'Welcome in monaco-vscode-api demo'
},
productConfiguration: {
nameShort: 'monaco-vscode-api',
nameLong: 'monaco-vscode-api',
extensionsGallery: {
serviceUrl: 'https://open-vsx.org/vscode/gallery',
itemUrl: 'https://open-vsx.org/vscode/item',
Expand Down Expand Up @@ -322,5 +325,66 @@ export const commonServices: IEditorOverrideServices = {
...getSpeechServiceOverride(),
...getSurveyServiceOverride(),
...getUpdateServiceOverride(),
...getExplorerServiceOverride()
...getExplorerServiceOverride(),
...getLocalizationServiceOverride({
async clearLocale () {
const url = new URL(window.location.href)
url.searchParams.delete('locale')
window.history.pushState(null, '', url.toString())
},
async setLocale (id) {
const url = new URL(window.location.href)
url.searchParams.set('locale', id)
window.history.pushState(null, '', url.toString())
},
availableLanguages: [{
locale: 'en',
languageName: 'English'
}, {
locale: 'cs',
languageName: 'Czech'
}, {
locale: 'de',
languageName: 'German'
}, {
locale: 'es',
languageName: 'Spanish'
}, {
locale: 'fr',
languageName: 'French'
}, {
locale: 'it',
languageName: 'Italian'
}, {
locale: 'ja',
languageName: 'Japanese'
}, {
locale: 'ko',
languageName: 'Korean'
}, {
locale: 'pl',
languageName: 'Polish'
}, {
locale: 'pt-br',
languageName: 'Portuguese (Brazil)'
}, {
locale: 'qps-ploc',
languageName: 'Pseudo Language'
}, {
locale: 'ru',
languageName: 'Russian'
}, {
locale: 'tr',
languageName: 'Turkish'
}, {
locale: 'zh-hans',
languageName: 'Chinese (Simplified)'
}, {
locale: 'zh-hant',
languageName: 'Chinese (Traditional)'
}, {
locale: 'en',
languageName: 'English'
}]
})
}
19 changes: 0 additions & 19 deletions demo/src/setup.views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,6 @@ container.innerHTML = `
<br />
<button id="togglePanel">Toggle Panel</button>
<button id="toggleAuxiliary">Toggle Secondary Panel</button>
<br />
<label for="localeSelect">Language:</label>
<select id="localeSelect">
<option value="">English</option>
<option value="cs">Czech</option>
<option value="de">German</option>
<option value="es">Spanish</option>
<option value="fr">French</option>
<option value="it">Italian</option>
<option value="ja">Japanese</option>
<option value="ko">Korean</option>
<option value="pl">Polish</option>
<option value="pt-br">Portuguese (Brazil)</option>
<option value="qps-ploc">Pseudo Language</option>
<option value="ru">Russian</option>
<option value="tr">Turkish</option>
<option value="zh-hans">Chinese (Simplified)</option>
<option value="zh-hant">Chinese (Traditional)</option>
</select>
</div>
<div style="display: flex; flex: none; border: 1px solid var(--vscode-editorWidget-border);">
<div id="sidebar-right" style="max-width: 500px"></div>
Expand Down
19 changes: 0 additions & 19 deletions demo/src/setup.workbench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,6 @@ buttons.innerHTML = `
<br />
<button id="togglePanel">Toggle Panel</button>
<button id="toggleAuxiliary">Toggle Secondary Panel</button>
<br />
<label for="localeSelect">Language:</label>
<select id="localeSelect">
<option value="">English</option>
<option value="cs">Czech</option>
<option value="de">German</option>
<option value="es">Spanish</option>
<option value="fr">French</option>
<option value="it">Italian</option>
<option value="ja">Japanese</option>
<option value="ko">Korean</option>
<option value="pl">Polish</option>
<option value="pt-br">Portuguese (Brazil)</option>
<option value="qps-ploc">Pseudo Language</option>
<option value="ru">Russian</option>
<option value="tr">Turkish</option>
<option value="zh-hans">Chinese (Simplified)</option>
<option value="zh-hant">Chinese (Traditional)</option>
</select>
</div>
`
document.body.append(buttons)
Expand Down
2 changes: 1 addition & 1 deletion rollup/rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const REMOVE_WORKBENCH_CONTRIBUTIONS = new Set([
/**
* root files that should never be extracted from the main package to a service override package
*/
const SHARED_ROOT_FILES_BETWEEN_PACKAGES = ['services.js', 'extensions.js', 'monaco.js', 'assets.js', 'lifecycle.js', 'workbench.js', 'missing-services.js']
const SHARED_ROOT_FILES_BETWEEN_PACKAGES = ['services.js', 'extensions.js', 'monaco.js', 'assets.js', 'lifecycle.js', 'workbench.js', 'missing-services.js', 'l10n.js']
/**
* Files to expose in the editor-api package (just exporting everyting from the corresponding VSCode file)
* for compability with libraries that import internal monaco-editor modules
Expand Down
2 changes: 1 addition & 1 deletion rollup/rollup.language-packs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export default rollup.defineConfig([
return `
import { registerLocalization } from 'vscode/l10n'
import content from '${path.resolve(id, mainTranslation.path)}'
registerLocalization('${mainLocalization.languageId}', content, {
registerLocalization('${packageJson.publisher}.${packageJson.name}', '${mainLocalization.languageId}', content, {
${Object.entries(translationAssets).map(([id, assetRef]) => ` '${id}': new URL(import.meta.ROLLUP_FILE_URL_${assetRef}, import.meta.url).href`).join(',\n')}
})
`
Expand Down
22 changes: 20 additions & 2 deletions src/l10n.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,39 @@
import { setLocale, isInitialized } from 'vs/nls'

const extensionTranslationsUri: Record<string, Record<string, string>> = {}
let currentLocaleExtensionId: string | undefined
let availableLocales: Set<string> = new Set()

function registerLocalization (language: string, main: Record<string, Record<string, string>>, extensionTranslationsUris: Record<string, string>): void {
function setAvailableLocales (locales: Set<string>): void {
availableLocales = locales
}

function isLocaleAvailable (locale: string): boolean {
return availableLocales.has(locale)
}

function registerLocalization (extensionId: string, language: string, main: Record<string, Record<string, string>>, extensionTranslationsUris: Record<string, string>): void {
if (isInitialized()) {
console.error('Some parts of VSCode are already initialized, make sure the language pack is loaded before anything else or some translations will be missing')
}
setLocale(language, main)

extensionTranslationsUri[language] = extensionTranslationsUris
currentLocaleExtensionId = extensionId
}

function getBuiltInExtensionTranslationsUris (language: string): Record<string, string> | undefined {
return extensionTranslationsUri[language]
}

function getExtensionIdProvidingCurrentLocale (): string | undefined {
return currentLocaleExtensionId
}

export {
registerLocalization,
getBuiltInExtensionTranslationsUris
getBuiltInExtensionTranslationsUris,
getExtensionIdProvidingCurrentLocale,
setAvailableLocales,
isLocaleAvailable
}
14 changes: 7 additions & 7 deletions src/missing-services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,9 @@ import { IAuthenticationAccessService } from 'vs/workbench/services/authenticati
import { IAuthenticationUsageService } from 'vs/workbench/services/authentication/browser/authenticationUsageService'
import { ICustomEditorLabelService } from 'vs/workbench/services/editor/common/customEditorLabelService'
import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'
import { getBuiltInExtensionTranslationsUris } from './l10n'
import { createInstanceCapabilityEventMultiplexer } from 'vs/workbench/contrib/terminal/browser/terminalEvents'
import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'
import { getBuiltInExtensionTranslationsUris, getExtensionIdProvidingCurrentLocale } from './l10n'
import { unsupported } from './tools'

registerSingleton(ILoggerService, class NullLoggerService extends AbstractLoggerService {
Expand Down Expand Up @@ -1656,10 +1658,8 @@ registerSingleton(ITerminalService, class TerminalService implements ITerminalSe
onAnyInstanceProcessIdReady = Event.None
onAnyInstanceSelectionChange = Event.None
onAnyInstanceTitleChange = Event.None
createOnInstanceEvent = unsupported
createOnInstanceCapabilityEvent = unsupported
onInstanceEvent = unsupported
onInstanceCapabilityEvent = unsupported
createOnInstanceEvent = () => Event.None
createOnInstanceCapabilityEvent = <T extends TerminalCapability, K>(capabilityId: T) => createInstanceCapabilityEventMultiplexer<T, K>([], Event.None, Event.None, capabilityId, () => Event.None)
createDetachedTerminal = unsupported

onDidChangeSelection = Event.None
Expand Down Expand Up @@ -2466,8 +2466,8 @@ registerSingleton(IInteractiveDocumentService, class InteractiveDocumentService

registerSingleton(IActiveLanguagePackService, class ActiveLanguagePackService implements IActiveLanguagePackService {
readonly _serviceBrand: undefined
getExtensionIdProvidingCurrentLocale () {
return Promise.resolve(undefined)
async getExtensionIdProvidingCurrentLocale (): Promise<string | undefined> {
return getExtensionIdProvidingCurrentLocale()
}
}, InstantiationType.Eager)

Expand Down
16 changes: 14 additions & 2 deletions src/service-override/extensionGallery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'
import { IExtensionGalleryService, IExtensionManagementService, IExtensionTipsService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'
import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'
import { IExtension as IContribExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'
import { getLocale } from 'vs/platform/languagePacks/common/languagePacks'
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'
import { IExtensionManagementServerService, IWebExtensionsScannerService, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'
import { ExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagementServerService'
Expand Down Expand Up @@ -33,6 +34,7 @@ import { IExtensionFeaturesManagementService } from 'vs/workbench/services/exten
import { ExtensionFeaturesManagementService } from 'vs/workbench/services/extensionManagement/common/extensionFeaturesManagemetService'
import { registerAssets } from '../assets'
import { getExtensionManifests } from '../extensions'
import { isLocaleAvailable } from '../l10n'
import 'vs/workbench/contrib/extensions/browser/extensions.contribution'
import 'vs/workbench/contrib/extensions/browser/extensions.web.contribution'

Expand Down Expand Up @@ -78,6 +80,16 @@ class CustomBuiltinExtensionsScannerService implements IBuiltinExtensionsScanner
}
}

class ExtensionsWorkbenchServiceOverride extends ExtensionsWorkbenchService {
override canSetLanguage (extension: IContribExtension): boolean {
if (super.canSetLanguage(extension)) {
const locale = getLocale(extension.gallery!)!
return isLocaleAvailable(locale)
}
return false
}
}

export interface ExtensionGalleryOptions {
/**
* Whether we should only allow for web extensions to be installed, this is generally
Expand All @@ -90,7 +102,7 @@ export default function getServiceOverride (options: ExtensionGalleryOptions = {
return {
[IExtensionGalleryService.toString()]: new SyncDescriptor(ExtensionGalleryService, [], true),
[IGlobalExtensionEnablementService.toString()]: new SyncDescriptor(GlobalExtensionEnablementService, [], true),
[IExtensionsWorkbenchService.toString()]: new SyncDescriptor(ExtensionsWorkbenchService, [], true),
[IExtensionsWorkbenchService.toString()]: new SyncDescriptor(ExtensionsWorkbenchServiceOverride, [], true),
[IExtensionManagementServerService.toString()]: new SyncDescriptor(ExtensionManagementServerServiceOverride, [options.webOnly], true),
[IExtensionRecommendationsService.toString()]: new SyncDescriptor(ExtensionRecommendationsService, [], true),
[IExtensionRecommendationNotificationService.toString()]: new SyncDescriptor(ExtensionRecommendationNotificationService, [], true),
Expand Down
Loading