Skip to content

Commit 7c20c26

Browse files
committed
fix(@angular-devkit/build-angular): insert locale data when localizing
1 parent e70a2b0 commit 7c20c26

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

packages/angular_devkit/build_angular/src/utils/i18n-options.ts

+46-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
import { BuilderContext } from '@angular-devkit/architect';
9-
import { json, virtualFs } from '@angular-devkit/core';
9+
import { json } from '@angular-devkit/core';
1010
import * as fs from 'fs';
1111
import * as os from 'os';
1212
import * as path from 'path';
@@ -19,7 +19,10 @@ import { createTranslationLoader } from './load-translations';
1919
export interface I18nOptions {
2020
inlineLocales: Set<string>;
2121
sourceLocale: string;
22-
locales: Record<string, { file: string; format?: string; translation?: unknown }>;
22+
locales: Record<
23+
string,
24+
{ file: string; format?: string; translation?: unknown; localeDataPath?: string }
25+
>;
2326
flatOutput?: boolean;
2427
readonly shouldInline: boolean;
2528
}
@@ -127,6 +130,13 @@ export async function configureI18nBuild<T extends BrowserBuilderSchema | Server
127130
}
128131

129132
if (i18n.inlineLocales.size > 0) {
133+
const localeDataBasePath = findLocaleBaseDataPath();
134+
if (!localeDataBasePath) {
135+
throw new Error(
136+
`Unable to find locale data within '@angular/common'. Please ensure '@angular/common' is installed.`,
137+
);
138+
}
139+
130140
// Load locales
131141
const loader = await createTranslationLoader();
132142
const projectRoot = path.join(context.workspaceRoot, (metadata.root as string) || '');
@@ -145,6 +155,15 @@ export async function configureI18nBuild<T extends BrowserBuilderSchema | Server
145155

146156
desc.format = result.format;
147157
desc.translation = result.translation;
158+
159+
const localeDataPath = findLocaleDataPath(locale, localeDataBasePath);
160+
if (!localeDataPath) {
161+
context.logger.warn(
162+
`Locale data for '${locale}' cannot be found. No locale data will be included for this locale.`,
163+
);
164+
} else {
165+
desc.localeDataPath = localeDataPath;
166+
}
148167
}
149168
}
150169

@@ -197,3 +216,28 @@ function mergeDeprecatedI18nOptions(
197216

198217
return i18n;
199218
}
219+
220+
function findLocaleBaseDataPath(): string | null {
221+
try {
222+
const commonPath = path.dirname(require.resolve('@angular/common/package.json'));
223+
const localesPath = path.join(commonPath, 'locales/global');
224+
225+
if (!fs.existsSync(localesPath)) {
226+
return null;
227+
}
228+
229+
return localesPath;
230+
} catch {
231+
return null;
232+
}
233+
}
234+
235+
function findLocaleDataPath(locale: string, basePath: string): string | null {
236+
const localeDataPath = path.join(basePath, locale);
237+
238+
if (!fs.existsSync(localeDataPath)) {
239+
return null;
240+
}
241+
242+
return localeDataPath;
243+
}

packages/angular_devkit/build_angular/src/utils/process-bundle.ts

+24
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,14 @@ export async function inlineLocales(options: InlineOptions) {
540540
const setLocaleText = `var $localize=Object.assign(void 0===$localize?{}:$localize,{locale:"${locale}"});`;
541541
contentClone = content.clone();
542542
content.prepend(setLocaleText);
543+
544+
// If locale data is provided, load it, if not already, and then prepend to file
545+
const localeDataPath = i18n.locales[locale] && i18n.locales[locale].localeDataPath;
546+
if (localeDataPath) {
547+
const localDataContent = loadLocaleData(localeDataPath, true);
548+
// The semicolon ensures that there is no syntax error between statements
549+
content.prepend(localDataContent + ';');
550+
}
543551
}
544552

545553
const output = content.toString();
@@ -667,3 +675,19 @@ function findLocalizePositions(
667675

668676
return positions;
669677
}
678+
679+
function loadLocaleData(path: string, optimize: boolean): string {
680+
// The path is validated during option processing before the build starts
681+
const content = fs.readFileSync(path, 'utf8');
682+
683+
if (optimize) {
684+
const result = terserMangle(content, {
685+
compress: true,
686+
ecma: 5,
687+
});
688+
689+
return result.code;
690+
}
691+
692+
return content;
693+
}

0 commit comments

Comments
 (0)