fix: extension configure i18n support variable/i18n Readme

This commit is contained in:
scarqin 2022-07-19 20:53:44 +08:00
parent b679a5834f
commit f2e1cebac0
12 changed files with 166 additions and 173 deletions

View File

@ -6,7 +6,7 @@ on:
jobs:
synchronize-with-crowdin:
if: startsWith(github.event.head_commit.message , 'i18n')
if: contains(github.event.head_commit.message , 'i18n')
runs-on: ubuntu-latest
steps:
- name: Checkout

View File

@ -1,89 +0,0 @@
// import { ipcRenderer, app } from 'electron';
// import { isNotEmpty } from 'eo/shared/common/common';
// import * as fs from 'fs';
// import * as path from 'path';
// import {
// StorageRes,
// StorageResStatus,
// StorageHandleArgs,
// StorageProcessType,
// } from '../../../../workbench/browser/src/app/shared/services/storage/index.model';
// import { IndexedDBStorage } from '../../../../workbench/browser/src/app/shared/services/storage/IndexedDB/lib/index';
// class StorageService {
// private ipcRenderer: typeof ipcRenderer;
// private app: typeof app;
// private fs: typeof fs;
// private path: typeof path;
// constructor() {
// this.ipcRenderer = window.require('electron').ipcRenderer;
// this.app = window.require('electron').app;
// this.fs = window.require('fs');
// this.path = window.require('path');
// this.storageListen();
// }
// /**
// * 存储监听处理
// * @param args
// */
// private storageListenHandle(args: StorageHandleArgs): void {
// const action: string = args.action || undefined;
// const handleResult: StorageRes = {
// status: StorageResStatus.invalid,
// data: undefined,
// callback: args.callback || null,
// };
// if (IndexedDBStorage && IndexedDBStorage[action] && typeof IndexedDBStorage[action] === 'function') {
// IndexedDBStorage[action](...args.params).subscribe(
// (result: any) => {
// handleResult.data = result;
// if (isNotEmpty(result)) {
// handleResult.status = StorageResStatus.success;
// } else {
// handleResult.status = StorageResStatus.empty;
// }
// this.storageListenHandleNotify(args.type, handleResult);
// },
// (error: any) => {
// handleResult.status = StorageResStatus.error;
// this.storageListenHandleNotify(args.type, handleResult);
// }
// );
// } else {
// this.storageListenHandleNotify(args.type, handleResult);
// }
// }
// /**
// * 数据存储监听通知返回
// * @param type
// * @param result
// */
// private storageListenHandleNotify(type: string, result: StorageRes): void {
// try {
// if (StorageProcessType.default === type) {
// this.ipcRenderer.send('eo-storage', { type: 'result', result: result });
// } else if (StorageProcessType.sync === type) {
// const storageTemp = this.path.join(this.app.getPath('home'), '.eo', 'tmp.storage');
// this.fs.writeFileSync(storageTemp, JSON.stringify(result));
// } else if (StorageProcessType.remote === type) {
// window.require('@electron/remote').getGlobal('shareObject').storageResult = result;
// }
// } catch (e) {
// console.log(e);
// }
// }
// /**
// * 开启数据存储监听
// * @returns
// */
// private storageListen(): void {
// this.ipcRenderer.on('eo-storage', (event, args: StorageHandleArgs) => this.storageListenHandle(args));
// }
// isElectron(): boolean {
// return !!(window && window.process && window.process.type);
// }
// }

View File

@ -23,18 +23,39 @@ export class TranslateService {
* Transalte package.json variable ${} to locale text
*/
translateVariableKey() {
let that = this;
Object.keys(this.module.features).forEach((name) => {
let feature = that.module.features[name];
Object.keys(feature).forEach((childName) => {
if (typeof feature[childName] !== 'string') return;
that.module.features[name][childName] = feature[childName].replace(/\$\{(.+)\}/g, (match, rest) => {
let replacement = match;
replacement = that.locale[rest] || replacement;
return replacement;
});
});
this.translateObject(this.locale, this.module.features, {
currentLevel: 0,
maxLevel: 4,
});
return this;
}
/**
* Loop translate object
* @param locale
* @param origin
* @param opts.maxLevel loop object level
*/
private translateObject(locale, origin, opts) {
if (opts.currentLevel >= opts.maxLevel) return;
Object.keys(origin).forEach((name) => {
if (typeof origin[name] !== 'string') {
let newOpts = { maxLevel: opts.maxLevel, currentLevel: opts.currentLevel + 1 };
this.translateObject(locale, origin[name], newOpts);
return;
}
origin[name] = this.translateString(locale, origin[name]);
});
}
/**
* Translate primitive data types(string/numebr/..)
* @param locale
* @param variable
*/
private translateString(locale, variable) {
return variable.replace(/\$\{(.+)\}/g, (match, rest) => {
let replacement = match;
replacement = locale[rest] || replacement;
return replacement;
});
}
}

View File

@ -9,39 +9,48 @@
</div>
<section class="h-full p-4 max-w-[80vw] mx-auto">
<div class="flex">
<i class="block w-24 h-24 mr-8 bg-center bg-no-repeat bg-cover border rounded-lg bd_all"
[ngStyle]="{ 'background-image': 'url(' + (extensionDetail?.logo || '') + ')' }"></i>
<i
class="block w-24 h-24 mr-8 bg-center bg-no-repeat bg-cover border rounded-lg bd_all"
[ngStyle]="{ 'background-image': 'url(' + (extensionDetail?.logo || '') + ')' }"
></i>
<div class="flex flex-col flex-1">
<div class="flex flex-col">
<span class="mb-2 text-xl font-bold">{{ extensionDetail?.moduleName }}</span>
<p class="w-full h-20">{{ extensionDetail?.description }}</p>
</div>
<div class="flex">
</div>
<div class="flex"></div>
</div>
</div>
<div class="flex w-full mt-6 h-[calc(100vh-350px)]">
<div class="flex-auto">
<div class="flex-auto min-w-0">
<h2 class="text-lg font-bold" i18n>Intro</h2>
<!-- <nz-divider></nz-divider> -->
<div class="h-full overflow-auto markdown-desc">
<nz-skeleton [nzLoading]="introLoading" [nzActive]="true">
<eo-shadow-dom [text]="extensionDetail?.introduction" [options]="{ html: true }">
<eo-shadow-dom class="md-preview" [text]="extensionDetail?.introduction" [options]="{ html: true }">
</eo-shadow-dom>
</nz-skeleton>
</div>
</div>
<div class="w-[1px] bg-[#f2f2f2] mx-[10px]"></div>
<div class="w-[200px] 2xl:w-[250px] overflow-auto h-full">
<div class="shrink-0 w-[200px] 2xl:w-[250px] overflow-auto h-full">
<h2 class="text-lg font-bold" i18n>Install</h2>
<div class="flex items-center mt-[22px]" *ngIf="!extensionDetail?.installed">
<button *ngIf="isElectron" nz-button nzType="primary" nzBlock nzSize="large" [nzLoading]="isOperating"
(click)="manageExtension('install', extensionDetail?.name)">
<button
*ngIf="isElectron"
nz-button
nzType="primary"
nzBlock
nzSize="large"
[nzLoading]="isOperating"
(click)="manageExtension('install', extensionDetail?.name)"
i18n
>
Install
</button>
<div *ngIf="!isElectron">
<button nz-button nzType="primary" nz-dropdown [nzDropdownMenu]="download" class="!w-full" i18n>Download Client
<button nz-button nzType="primary" nz-dropdown [nzDropdownMenu]="download" class="!w-full" i18n>
Download Client
</button>
<nz-dropdown-menu #download="nzDropdownMenu">
<ul nz-menu>
@ -56,16 +65,25 @@
</div>
</div>
</div>
<button *ngIf="extensionDetail?.installed" nz-button nzBlock nzType="primary" nzDanger nzSize="large"
[nzLoading]="isOperating" class="mt-[12px]" (click)="manageExtension('uninstall', extensionDetail?.name)" i18n>
<button
*ngIf="extensionDetail?.installed"
nz-button
nzBlock
nzType="primary"
nzDanger
nzSize="large"
[nzLoading]="isOperating"
class="mt-[12px]"
(click)="manageExtension('uninstall', extensionDetail?.name)"
i18n
>
Uninstall
</button>
<h2 class="text-lg font-bold mt-[30px]" i18n>Support</h2>
<nz-descriptions [nzColumn]="1" nzTitle="">
<nz-descriptions-item i18n-nzTitle nzTitle="Author">{{ extensionDetail?.author}}</nz-descriptions-item>
<nz-descriptions-item i18n-nzTitle nzTitle="Version">{{ extensionDetail?.version }}
</nz-descriptions-item>
<nz-descriptions-item i18n-nzTitle nzTitle="Author">{{ extensionDetail?.author }}</nz-descriptions-item>
<nz-descriptions-item i18n-nzTitle nzTitle="Version">{{ extensionDetail?.version }} </nz-descriptions-item>
</nz-descriptions>
</div>
</div>

View File

@ -4,23 +4,28 @@
padding-top: 20px;
}
}
}
::ng-deep .extension-detail {
.markdown-desc {
overflow: auto;
&::-webkit-scrollbar {
width:0;
}
&:hover::-webkit-scrollbar {
width:8px;
.md-preview {
img {
max-width: 800px;
}
}
}
.ant-descriptions-row > td {
padding-bottom: 8px;
}
.ant-descriptions-item-content,.ant-descriptions-item-label {
color: #999;
.extension-detail {
.markdown-desc {
overflow: auto;
&::-webkit-scrollbar {
width: 0;
}
&:hover::-webkit-scrollbar {
width: 8px;
}
}
.ant-descriptions-row > td {
padding-bottom: 8px;
}
.ant-descriptions-item-content,
.ant-descriptions-item-label {
color: #999;
}
}
}
}

View File

@ -4,6 +4,7 @@ import { ElectronService } from 'eo/workbench/browser/src/app/core/services';
import { EoExtensionInfo } from '../extension.model';
import { ResourceInfo } from '../../../shared/models/client.model';
import { ExtensionService } from '../extension.service';
import { LanguageService } from 'eo/workbench/browser/src/app/core/services/language/language.service';
@Component({
selector: 'eo-extension-detail',
@ -22,7 +23,8 @@ export class ExtensionDetailComponent implements OnInit {
private extensionService: ExtensionService,
private route: ActivatedRoute,
private router: Router,
private electronService: ElectronService
private electronService: ElectronService,
private language: LanguageService
) {
this.getDetail();
this.getInstaller();
@ -33,17 +35,26 @@ export class ExtensionDetailComponent implements OnInit {
this.route.snapshot.queryParams.name
);
if (!this.extensionDetail?.installed) {
await this.fetchReadme();
await this.fetchReadme(this.language.systemLanguage);
}
this.extensionDetail.introduction ||= $localize`This plugin has no documentation yet.`;
}
async fetchReadme() {
async fetchReadme(locale = '') {
//Default locale en-US
if (locale === 'en-US') locale = '';
try {
this.introLoading = true;
const response = await fetch(`https://unpkg.com/${this.extensionDetail.name}/README.md`);
const response = await fetch(
`https://unpkg.com/${this.extensionDetail.name}@${this.extensionDetail.version}/README.${
locale ? locale + '.' : ''
}md`
);
if (response.status === 200) {
this.extensionDetail.introduction = await response.text();
} else if (locale) {
//If locale README not find,fetch default locale(en-US)
this.fetchReadme();
}
} catch (error) {
} finally {
@ -92,7 +103,6 @@ export class ExtensionDetailComponent implements OnInit {
manageExtension(operate: string, id) {
this.isOperating = true;
console.log(this.isOperating);
/**
* * WARNING:Sending a synchronous message will block the whole
* renderer process until the reply is received, so use this method only as a last
@ -107,7 +117,7 @@ export class ExtensionDetailComponent implements OnInit {
}
case 'uninstall': {
this.extensionDetail.installed = !this.extensionService.uninstall(id);
this.fetchReadme();
this.fetchReadme(this.language.systemLanguage);
break;
}
}

View File

@ -30,28 +30,29 @@ export class ExtensionService {
private translateModule(module: ModuleInfo) {
const lang = this.language.systemLanguage;
const locale = module.i18n?.find((val) => val.locale === lang)?.package;
console.log(locale, module);
if (!locale) return module;
module = new TranslateService(module, locale).translate();
return module;
}
public async requestList() {
let result: any = await lastValueFrom(this.http.get(`${this.HOST}/list?locale=${this.language.systemLanguage}`));
let installList = this.getInstalledList();
result.data = [
...result.data,
...result.data.filter((val) => installList.every((childVal) => childVal.name !== val.name)),
//Local debug package
...this.getInstalledList().filter((val) => result.data.every((childVal) => childVal.name !== val.name)),
...installList,
];
console.log(result.data)
result.data = result.data.map((module) => this.translateModule(module));
return result;
}
async getDetail(id, name): Promise<any> {
let result = {};
let { code, data }: any = await this.requestDetail(name);
Object.assign(result, data);
if (this.localExtensions.has(id)) {
Object.assign(result, this.localExtensions.get(id), { installed: true });
}
let { code, data }: any = await this.requestDetail(name);
Object.assign(result, data);
return result;
}
/**

View File

@ -5,7 +5,7 @@
class="bd_all w-full min-h-[140px] p-5 rounded-lg flex flex-col flex-wrap items-center plugin-block hover:border-green-700 hover:shadow-lg transition-shadow duration-300"
*ngFor="let it of renderList" (click)="clickExtension(it)">
<div class="flex w-full">
<div class=" block w-[40px] h-[40px] rounded-lg bg-cover bg-center bg-no-repeat mr-[20px]"
<div class="shrink-0 block w-[40px] h-[40px] rounded-lg bg-cover bg-center bg-no-repeat mr-[20px]"
[ngClass]="{ 'bg-gray-100': it.logo }" [ngStyle]="{ 'background-image': 'url(' + (it.logo || '') + ')' }">
</div>

View File

@ -80,7 +80,7 @@
>
<eo-shadow-dom [text]="module.properties[field]?.description || ''"></eo-shadow-dom>
</div>
<nz-form-control nzErrorTip="请输入{{ module.properties[field]?.label }}" class="form-control">
<nz-form-control i18n-nzErrorTip nzErrorTip="请输入{{ module.properties[field]?.label }}" class="form-control">
<!-- 字符串类型 -->
<ng-container *ngIf="module.properties[field]?.type === 'string'">
<input
@ -88,6 +88,7 @@
nz-input
id="{{ field }}"
[disabled]="module.properties[field]?.disabled"
i18n-placeholder
placeholder="{{ module.properties[field]?.placeholder ?? '请输入' + module.properties[field]?.label }}"
formControlName="{{ field }}"
[(ngModel)]="settings[field]"

View File

@ -4,7 +4,13 @@ import MarkdownIt from 'markdown-it/dist/markdown-it';
@Component({
selector: 'eo-shadow-dom',
template: ` <div [innerHTML]="content"></div> `,
styles: [],
styles: [
`
img {
max-width: 600px;
}
`,
],
encapsulation: ViewEncapsulation.ShadowDom,
})
export class ShadowDomEncapsulationComponent implements OnInit {

View File

@ -14,7 +14,7 @@
<source>No mock found with ID <x id="PH" equiv-text="mockID"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/app.service.ts</context>
<context context-type="linenumber">45</context>
<context context-type="linenumber">46</context>
</context-group>
</trans-unit>
<trans-unit id="4580786032076405950" datatype="html">
@ -1284,53 +1284,60 @@
<context context-type="linenumber">37</context>
</context-group>
</trans-unit>
<trans-unit id="0cd99ecbc1878fe507d251f7097c296827591866" datatype="html">
<source>Download Client </source>
<trans-unit id="c10feff406d4b2759fa2b37a33ebf9cf9f31276f" datatype="html">
<source> Install </source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/extension/detail/extension-detail.component.html</context>
<context context-type="linenumber">44,45</context>
<context context-type="linenumber">48,50</context>
</context-group>
</trans-unit>
<trans-unit id="ff30254c1bb7fdda70783d33f81d0312159c4742" datatype="html">
<source> Download Client </source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/extension/detail/extension-detail.component.html</context>
<context context-type="linenumber">52,54</context>
</context-group>
</trans-unit>
<trans-unit id="634e9df42c9b62368a31d091e53ea60582bff569" datatype="html">
<source> The extensions can only be installed on the client at present. Please download the client first~ </source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/extension/detail/extension-detail.component.html</context>
<context context-type="linenumber">54,56</context>
<context context-type="linenumber">63,65</context>
</context-group>
</trans-unit>
<trans-unit id="2e67b8fac834cf48010053ad82372fed4c4e2ba9" datatype="html">
<source> Uninstall </source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/extension/detail/extension-detail.component.html</context>
<context context-type="linenumber">60,62</context>
<context context-type="linenumber">79,81</context>
</context-group>
</trans-unit>
<trans-unit id="b5629d298ff1a69b8db19a4ba2995c76b52da604" datatype="html">
<source>Support</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/extension/detail/extension-detail.component.html</context>
<context context-type="linenumber">64</context>
<context context-type="linenumber">83</context>
</context-group>
</trans-unit>
<trans-unit id="d7293b61e9088c66421df2e367e0ccc00346cf9f" datatype="html">
<source>Author</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/extension/detail/extension-detail.component.html</context>
<context context-type="linenumber">66</context>
<context context-type="linenumber">85</context>
</context-group>
</trans-unit>
<trans-unit id="8fe73a4787b8068b2ba61f54ab7e0f9af2ea1fc9" datatype="html">
<source>Version</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/extension/detail/extension-detail.component.html</context>
<context context-type="linenumber">67</context>
<context context-type="linenumber">86</context>
</context-group>
</trans-unit>
<trans-unit id="6031434056991752553" datatype="html">
<source>This plugin has no documentation yet.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/extension/detail/extension-detail.component.ts</context>
<context context-type="linenumber">38</context>
<context context-type="linenumber">40</context>
</context-group>
</trans-unit>
<trans-unit id="8852490424970169127" datatype="html">
@ -2784,18 +2791,32 @@
<context context-type="linenumber">103</context>
</context-group>
</trans-unit>
<trans-unit id="b20217b59531361f9ebc6df4c148399f76461f96" datatype="html">
<source>请输入<x id="INTERPOLATION" equiv-text="{{ module.properties[field]?.label }}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/shared/components/setting/setting.component.html</context>
<context context-type="linenumber">83</context>
</context-group>
</trans-unit>
<trans-unit id="872abb7923d5fcf1c5b3b60f3c469ea277a17f48" datatype="html">
<source><x id="INTERPOLATION" equiv-text="{{ module.properties[field]?.placeholder ?? &apos;请输入&apos; + module.properties[field]?.label }}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/shared/components/setting/setting.component.html</context>
<context context-type="linenumber">92</context>
</context-group>
</trans-unit>
<trans-unit id="0153ca63af84192825f056daf8503077a518d413" datatype="html">
<source> Switched to <x id="INTERPOLATION" equiv-text="{{ isRemote ? &apos;Localhost&apos; : &apos;Remote Server&apos; }}"/> </source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/shared/components/setting/setting.component.html</context>
<context context-type="linenumber">136,138</context>
<context context-type="linenumber">137,139</context>
</context-group>
</trans-unit>
<trans-unit id="e0d1ece694da029a20f5a97fbf3302f6213da891" datatype="html">
<source>No plugins are currently installed,<x id="START_LINK" ctype="x-a" equiv-text="&lt;a class=&quot;eo_link&quot; (click)=&quot;navToExtensionList()&quot;&gt;"/> go to install <x id="CLOSE_LINK" ctype="x-a" equiv-text="&lt;/a&gt;"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/shared/components/setting/setting.component.html</context>
<context context-type="linenumber">152</context>
<context context-type="linenumber">153</context>
</context-group>
</trans-unit>
<trans-unit id="3139147897029202869" datatype="html">

View File

@ -1,13 +1,12 @@
{
"rewrites": [
{
"source": "/:path((?!en/).*)",
"destination": "/en/:path*"
},
{
"source": "/:path((?!zh/).*)",
"destination": "/zh/:path*"
}
]
}
"rewrites": [
{
"source": "/:path((?!zh/).*)",
"destination": "/zh/:path*"
},
{
"source": "/:path((?!en/).*)",
"destination": "/en/:path*"
}
]
}