export function registerLocale(options: Schema): Rule { return (host: Tree) => { // 获取路径 const workspace = getWorkspace(host); const project = getProjectFromWorkspace(workspace, options.project); const appModulePath = getAppModulePath(host, getProjectMainFile(project)); const moduleSource = getSourceFile(host, appModulePath); // 获取add 时选择的zh_cn,en_us啥的就是一个字符串 const locale = getCompatibleLocal(options); // 拿到 zh en这种 const localePrefix = locale.split('_')[ 0 ]; // recorder可以理解成?快照,一个目录下多个文件组成的文件快照,re coder // 为什么要beginUpdate,实际上我的理解是拿appModulePath文件建立了快照 // 直到后文 host.commitUpdate(recorder);才会把快照作出的修改提交到tree上面 // 也可以理解成你的项目有git控制,在你commit之前你操作的是快照,理解理解 const recorder = host.beginUpdate(appModulePath); // 对快照的操作列表 // insertImport = import {xxx} from 'xxx'这种 // 结合代码看一下app.module.ts上面的import内容(上面图片) const changes = [ insertImport(moduleSource, appModulePath, 'NZ_I18N', 'ng-zorro-antd'), insertImport(moduleSource, appModulePath, locale, 'ng-zorro-antd'), insertImport(moduleSource, appModulePath, 'registerLocaleData', '@angular/common'), insertImport(moduleSource, appModulePath, localePrefix, `@angular/common/locales/${localePrefix}`, true), registerLocaleData(moduleSource, appModulePath, localePrefix), // 这个函数特殊,看下面 ...insertI18nTokenProvide(moduleSource, appModulePath, locale) ]; // 循环变更列表如果是insertChange(import)那么引入 changes.forEach((change) => { if (change instanceof InsertChange) { recorder.insertLeft(change.pos, change.toAdd); } }); // 提交变更到tree host.commitUpdate(recorder); // 返回tree给上一级函数 return host; }; } //上面说了,就是那个zh_CN/en_Us function getCompatibleLocal(options: Schema): string { const defaultLocal = 'en_US'; if (options.locale === options.i18n) { return options.locale; } else if (options.locale === defaultLocal) { console.log(); console.log(`${chalk.bgYellow('WARN')} ${chalk.cyan('--i18n')} option will be deprecated, ` + `use ${chalk.cyan('--locale')} instead`); return options.i18n; } else { return options.locale || defaultLocal; } } // 这个函数主要是为了生成调用angular本地化的代码registerLocaleData(zh); function registerLocaleData(moduleSource: ts.SourceFile, modulePath: string, locale: string): Change { ... if (registerLocaleDataFun.length === 0) { // 最核心的要在app.module中加入registerLocaleData(zh);才能把本地化做到angular上面 return insertAfterLastOccurrence(allImports, `\n\nregisterLocaleData(${locale});`, modulePath, 0) as InsertChange; } ... } * 这个change在change列表略特殊 * @param moduleSource module文件 * @param modulePath module路径 * @param locale zh */ function insertI18nTokenProvide(moduleSource: ts.SourceFile, modulePath: string, locale: string): Change[] { const metadataField = 'providers'; // 获取app.module中NgModule注释的内容 //{ // declarations: [ // AppComponent // ], // imports: [ // BrowserModule, // AppRoutingModule, // NgZorroAntdModule, // FormsModule, // HttpClientModule, // BrowserAnimationsModule // ], // providers: [{ provide: NZ_I18N, useValue: zh_CN }], // bootstrap: [AppComponent] // } const nodes = getDecoratorMetadata(moduleSource, 'NgModule', '@angular/core'); // 生成一个provide到app.module中的ngModule注释中,生成到providers数组中 **的操作**(只是生成一个动作)还没应用到文件上 const addProvide = addSymbolToNgModuleMetadata(moduleSource, modulePath, 'providers', `{ provide: NZ_I18N, useValue: ${locale} }`, null); let node: any = nodes[ 0 ]; // tslint:disable-line:no-any // 然后下面开始做了一堆校验工作 if (!node) { return []; } const matchingProperties: ts.ObjectLiteralElement[] = (node as ts.ObjectLiteralExpression).properties .filter(prop => prop.kind === ts.SyntaxKind.PropertyAssignment) .filter((prop: ts.PropertyAssignment) => { const name = prop.name; switch (name.kind) { case ts.SyntaxKind.Identifier: return (name as ts.Identifier).getText(moduleSource) === metadataField; case ts.SyntaxKind.StringLiteral: return (name as ts.StringLiteral).text === metadataField; } return false; }); if (!matchingProperties) { return []; } if (matchingProperties.length) { const assignment = matchingProperties[ 0 ] as ts.PropertyAssignment; if (assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) { return []; } const arrLiteral = assignment.initializer as ts.ArrayLiteralExpression; if (arrLiteral.elements.length === 0) { return addProvide; } else { node = arrLiteral.elements.filter(e => e.getText && e.getText().includes('NZ_I18N')); if (node.length === 0) { return addProvide; } else { console.log(); console.log(chalk.yellow(`Could not provide the locale token to your app.module file (${chalk.blue(modulePath)}).` + `because there is already a locale token in provides.`)); console.log(chalk.yellow(`Please manually add the following code to your provides:`)); console.log(chalk.cyan(`{ provide: NZ_I18N, useValue: ${locale} }`)); return []; } } } else { // 如果都没什么大问题,则把增加Provide的动作返回到changes列表,等待commit然后作出更改动作 return addProvide; } }
参考文章