├── libs
├── .gitkeep
├── app
│ ├── src
│ │ ├── web
│ │ │ ├── .xplatframework
│ │ │ ├── index.ts
│ │ │ ├── scss
│ │ │ │ ├── _variables.scss
│ │ │ │ ├── package.json
│ │ │ │ ├── _tags.scss
│ │ │ │ ├── _index.scss
│ │ │ │ └── _spacing.scss
│ │ │ └── src
│ │ │ │ ├── index.ts
│ │ │ │ ├── core.module.spec.ts
│ │ │ │ ├── services
│ │ │ │ ├── index.ts
│ │ │ │ ├── tokens.ts
│ │ │ │ ├── log.service.ts
│ │ │ │ └── window.service.ts
│ │ │ │ └── core.module.ts
│ │ ├── electron
│ │ │ ├── .xplatframework
│ │ │ ├── index.ts
│ │ │ └── src
│ │ │ │ ├── index.ts
│ │ │ │ ├── services
│ │ │ │ ├── index.ts
│ │ │ │ └── electron.service.ts
│ │ │ │ └── core.module.ts
│ │ └── test-setup.ts
│ ├── tsconfig.json
│ ├── tslint.json
│ ├── tsconfig.spec.json
│ └── tsconfig.lib.json
├── cli
│ ├── src
│ │ ├── test-setup.ts
│ │ ├── locl
│ │ ├── index.ts
│ │ ├── lib
│ │ │ └── cli.module.ts
│ │ ├── main.ts
│ │ ├── tsconfig.json
│ │ └── cmds
│ │ │ ├── extract
│ │ │ ├── translation_files
│ │ │ │ ├── translation_serializer.ts
│ │ │ │ ├── json_translation_serializer.ts
│ │ │ │ ├── xmb_translation_serializer.ts
│ │ │ │ ├── xtb_translation_serializer.ts
│ │ │ │ ├── xliff1_translation_serializer.ts
│ │ │ │ ├── xml_file.ts
│ │ │ │ └── xliff2_translation_serializer.ts
│ │ │ ├── extractor.ts
│ │ │ ├── source_files
│ │ │ │ ├── es2015_extract_plugin.ts
│ │ │ │ └── es5_extract_plugin.ts
│ │ │ └── extract.ts
│ │ │ ├── convert
│ │ │ ├── translations.ts
│ │ │ ├── message_serialization
│ │ │ │ ├── message_renderer.ts
│ │ │ │ ├── target_message_renderer.ts
│ │ │ │ └── message_serializer.ts
│ │ │ ├── translation_parsers
│ │ │ │ ├── translation_parse_error.ts
│ │ │ │ ├── translation_parser.ts
│ │ │ │ ├── simple_json_translation_parser.ts
│ │ │ │ ├── xtb_translation_parser.ts
│ │ │ │ ├── xliff2_translation_parser.ts
│ │ │ │ ├── xliff1_translation_parser.ts
│ │ │ │ └── translation_utils.ts
│ │ │ └── convert.ts
│ │ │ ├── convert.ts
│ │ │ ├── extract.ts
│ │ │ └── common
│ │ │ ├── diagnostics.ts
│ │ │ └── file_utils.ts
│ ├── tsconfig.json
│ ├── tslint.json
│ ├── ng-package.json
│ ├── tsconfig.spec.json
│ ├── tsconfig.lib.json
│ ├── package.json
│ ├── test
│ │ └── cmds
│ │ │ ├── convert.spec.ts
│ │ │ └── extract.spec.ts
│ └── README.md
├── common
│ ├── src
│ │ ├── test-setup.ts
│ │ ├── index.ts
│ │ └── lib
│ │ │ ├── common.module.ts
│ │ │ └── common.module.spec.ts
│ ├── package.json
│ ├── ng-package.json
│ ├── tsconfig.json
│ ├── tslint.json
│ ├── tsconfig.spec.json
│ └── tsconfig.lib.json
├── core
│ ├── src
│ │ ├── test-setup.ts
│ │ ├── index.ts
│ │ └── lib
│ │ │ ├── interfaces.ts
│ │ │ ├── global.ts
│ │ │ ├── init.ts
│ │ │ └── core.module.ts
│ ├── package.json
│ ├── ng-package.json
│ ├── tsconfig.json
│ ├── tslint.json
│ ├── tsconfig.spec.json
│ ├── tsconfig.lib.json
│ └── README.md
├── utils
│ ├── src
│ │ ├── test-setup.ts
│ │ ├── index.ts
│ │ └── lib
│ │ │ ├── objects.ts
│ │ │ ├── angular.ts
│ │ │ └── platform.ts
│ ├── package.json
│ ├── ng-package.json
│ ├── tsconfig.json
│ ├── tslint.json
│ ├── tsconfig.spec.json
│ └── tsconfig.lib.json
└── scss
│ ├── package.json
│ ├── _index.scss
│ └── _variables.scss
├── apps
├── .gitkeep
├── web
│ ├── src
│ │ ├── assets
│ │ │ ├── .gitkeep
│ │ │ └── img
│ │ │ │ ├── bg.jpg
│ │ │ │ ├── bg.png
│ │ │ │ ├── favicon.ico
│ │ │ │ ├── locl-full.png
│ │ │ │ ├── locl-social.png
│ │ │ │ ├── favicon-16x16.png
│ │ │ │ ├── favicon-32x32.png
│ │ │ │ ├── mstile-150x150.png
│ │ │ │ ├── apple-touch-icon.png
│ │ │ │ ├── android-chrome-192x192.png
│ │ │ │ ├── android-chrome-256x256.png
│ │ │ │ ├── browserconfig.xml
│ │ │ │ ├── site.webmanifest
│ │ │ │ └── safari-pinned-tab.svg
│ │ ├── app
│ │ │ ├── app.component.scss
│ │ │ ├── app.electron.module.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.component.ts
│ │ │ └── app.component.spec.ts
│ │ ├── test-setup.ts
│ │ ├── favicon.ico
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── main.ts
│ │ ├── main.electron.ts
│ │ ├── styles.scss
│ │ ├── index.html
│ │ └── polyfills.ts
│ ├── tsconfig.electron.json
│ ├── tslint.json
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ ├── jest.config.js
│ └── browserslist
├── demo
│ ├── src
│ │ ├── assets
│ │ │ ├── .gitkeep
│ │ │ └── i18n
│ │ │ │ ├── lazy.fr.json
│ │ │ │ └── fr.json
│ │ ├── favicon.ico
│ │ ├── test-setup.ts
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── styles.scss
│ │ ├── app
│ │ │ ├── app.component.html
│ │ │ ├── lazy-loaded
│ │ │ │ ├── lazy-loaded.component.ts
│ │ │ │ └── lazy-loaded.module.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.component.ts
│ │ │ ├── app-routing.module.ts
│ │ │ ├── app.component.spec.ts
│ │ │ └── app.component.scss
│ │ ├── index.html
│ │ ├── i18n-setup.ts
│ │ ├── main.ts
│ │ └── polyfills.ts
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ ├── tslint.json
│ ├── tsconfig.app.json
│ ├── jest.config.js
│ └── browserslist
├── electron
│ ├── .gitignore
│ ├── src
│ │ ├── icons
│ │ │ ├── icon.ico
│ │ │ ├── icon.png
│ │ │ └── icon.icns
│ │ ├── package.json
│ │ └── index.ts
│ └── tsconfig.json
├── demo-e2e
│ ├── src
│ │ ├── support
│ │ │ ├── app.po.ts
│ │ │ ├── index.ts
│ │ │ └── commands.ts
│ │ ├── fixtures
│ │ │ └── example.json
│ │ ├── integration
│ │ │ └── app.spec.ts
│ │ └── plugins
│ │ │ └── index.js
│ ├── tslint.json
│ ├── tsconfig.json
│ ├── tsconfig.e2e.json
│ └── cypress.json
└── web-e2e
│ ├── src
│ ├── support
│ │ ├── app.po.ts
│ │ ├── index.ts
│ │ └── commands.ts
│ ├── fixtures
│ │ └── example.json
│ ├── integration
│ │ └── app.spec.ts
│ └── plugins
│ │ └── index.js
│ ├── tslint.json
│ ├── tsconfig.e2e.json
│ ├── tsconfig.json
│ └── cypress.json
├── tools
├── schematics
│ └── .gitkeep
├── tsconfig.tools.json
├── commitlint-plugin-body-content
│ ├── package.json
│ └── commitlint-plugin-body-content.js
├── web
│ └── postinstall.js
└── electron
│ └── postinstall.js
├── .prettierrc
├── .prettierignore
├── lerna.json
├── .vscode
└── extensions.json
├── testing
├── tsconfig.xplat.json
├── tsconfig.libs.spec.json
├── tsconfig.xplat.spec.json
├── test.xplat.ts
├── test.libs.ts
├── tsconfig.libs.json
└── karma.conf.js
├── jest.config.js
├── .editorconfig
├── commitlint.config.js
├── .github
└── FUNDING.yml
├── .gitignore
├── nx.json
├── tsconfig.json
├── README.md
├── tslint.json
└── decorate-angular-cli.js
/libs/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/apps/web/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tools/schematics/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/demo/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/web/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/electron/.gitignore:
--------------------------------------------------------------------------------
1 | *.js
2 | *.js.map
--------------------------------------------------------------------------------
/libs/app/src/web/.xplatframework:
--------------------------------------------------------------------------------
1 | angular
--------------------------------------------------------------------------------
/libs/app/src/electron/.xplatframework:
--------------------------------------------------------------------------------
1 | angular
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true
3 | }
4 |
--------------------------------------------------------------------------------
/libs/app/src/web/index.ts:
--------------------------------------------------------------------------------
1 | export * from './src';
2 |
--------------------------------------------------------------------------------
/apps/web/src/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular';
--------------------------------------------------------------------------------
/libs/app/src/electron/index.ts:
--------------------------------------------------------------------------------
1 | export * from './src';
2 |
--------------------------------------------------------------------------------
/libs/app/src/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular';
--------------------------------------------------------------------------------
/libs/cli/src/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular';
--------------------------------------------------------------------------------
/libs/common/src/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular';
--------------------------------------------------------------------------------
/libs/core/src/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular';
--------------------------------------------------------------------------------
/libs/utils/src/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular';
--------------------------------------------------------------------------------
/libs/cli/src/locl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | require('./main.js');
--------------------------------------------------------------------------------
/libs/common/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/common.module';
2 |
--------------------------------------------------------------------------------
/libs/app/src/web/scss/_variables.scss:
--------------------------------------------------------------------------------
1 | // web specific variables here...
2 |
--------------------------------------------------------------------------------
/apps/demo-e2e/src/support/app.po.ts:
--------------------------------------------------------------------------------
1 | export const getGreeting = () => cy.get('h1');
2 |
--------------------------------------------------------------------------------
/apps/web-e2e/src/support/app.po.ts:
--------------------------------------------------------------------------------
1 | export const getGreeting = () => cy.get('h1');
2 |
--------------------------------------------------------------------------------
/libs/core/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/core.module';
2 | export * from './lib/init';
3 |
--------------------------------------------------------------------------------
/libs/utils/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@locl/utils",
3 | "version": "0.0.1"
4 | }
5 |
--------------------------------------------------------------------------------
/apps/demo/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/demo/src/favicon.ico
--------------------------------------------------------------------------------
/apps/demo/src/test-setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular';
2 | import '@angular/localize/init';
3 |
--------------------------------------------------------------------------------
/apps/web/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/web/src/favicon.ico
--------------------------------------------------------------------------------
/libs/app/src/web/scss/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@locl/web-scss",
3 | "version": "1.0.0"
4 | }
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Add files here to ignore them from prettier formatting
2 |
3 | **/xplat/*/.xplatframework
--------------------------------------------------------------------------------
/apps/web/src/assets/img/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/web/src/assets/img/bg.jpg
--------------------------------------------------------------------------------
/apps/web/src/assets/img/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/web/src/assets/img/bg.png
--------------------------------------------------------------------------------
/apps/web/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/apps/demo/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/apps/demo/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/apps/electron/src/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/electron/src/icons/icon.ico
--------------------------------------------------------------------------------
/apps/electron/src/icons/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/electron/src/icons/icon.png
--------------------------------------------------------------------------------
/libs/app/src/web/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './services';
2 | export { CoreModule } from './core.module';
3 |
--------------------------------------------------------------------------------
/apps/electron/src/icons/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/electron/src/icons/icon.icns
--------------------------------------------------------------------------------
/apps/web/src/assets/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/web/src/assets/img/favicon.ico
--------------------------------------------------------------------------------
/libs/scss/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@locl/scss",
3 | "version": "1.0.0",
4 | "main": "_index.scss"
5 | }
6 |
--------------------------------------------------------------------------------
/apps/web/src/assets/img/locl-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/web/src/assets/img/locl-full.png
--------------------------------------------------------------------------------
/apps/web/src/assets/img/locl-social.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/web/src/assets/img/locl-social.png
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": [
3 | "dist/libs/*",
4 | "dist/apps/*"
5 | ],
6 | "version": "independent"
7 | }
8 |
--------------------------------------------------------------------------------
/libs/app/src/electron/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './services';
2 | export { LoclElectronCoreModule } from './core.module';
3 |
--------------------------------------------------------------------------------
/apps/web/src/assets/img/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/web/src/assets/img/favicon-16x16.png
--------------------------------------------------------------------------------
/apps/web/src/assets/img/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/web/src/assets/img/favicon-32x32.png
--------------------------------------------------------------------------------
/apps/web/src/assets/img/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/web/src/assets/img/mstile-150x150.png
--------------------------------------------------------------------------------
/libs/app/src/web/scss/_tags.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Element tag overrides
3 | */
4 | body {
5 | padding: 0;
6 | margin: 0;
7 | }
8 |
--------------------------------------------------------------------------------
/libs/utils/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/angular';
2 | export * from './lib/objects';
3 | export * from './lib/platform';
4 |
--------------------------------------------------------------------------------
/apps/demo-e2e/src/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io"
4 | }
5 |
--------------------------------------------------------------------------------
/apps/web-e2e/src/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io"
4 | }
5 |
--------------------------------------------------------------------------------
/apps/web/src/assets/img/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/web/src/assets/img/apple-touch-icon.png
--------------------------------------------------------------------------------
/apps/web/src/assets/img/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/web/src/assets/img/android-chrome-192x192.png
--------------------------------------------------------------------------------
/apps/web/src/assets/img/android-chrome-256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loclapp/locl/HEAD/apps/web/src/assets/img/android-chrome-256x256.png
--------------------------------------------------------------------------------
/apps/demo-e2e/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": [],
4 | "linterOptions": {
5 | "exclude": ["!**/*"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/apps/web-e2e/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": [],
4 | "linterOptions": {
5 | "exclude": ["!**/*"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/apps/web/tsconfig.electron.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [
4 | "src/main.electron.ts",
5 | "src/polyfills.ts"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/apps/demo/src/assets/i18n/lazy.fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "locale": "fr",
3 | "translations": {
4 | "2945825304347416089": "Chargement asynchrone fonctionne !"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/libs/common/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@locl/common",
3 | "version": "0.0.1",
4 | "peerDependencies": {
5 | "@angular/core": ">=9.0.0-next.5"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/libs/common/src/lib/common.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 |
3 | @NgModule({
4 | imports: []
5 | })
6 | export class LoclCommonModule {
7 | }
8 |
--------------------------------------------------------------------------------
/libs/cli/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './lib/cli.module';
2 | export * from './cmds/extract/extract';
3 | export * from './cmds/convert/convert';
4 | export * from './main';
5 |
--------------------------------------------------------------------------------
/apps/demo-e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": ["cypress", "node"]
5 | },
6 | "include": ["**/*.ts"]
7 | }
8 |
--------------------------------------------------------------------------------
/libs/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@locl/core",
3 | "version": "1.0.1",
4 | "license": "AGPL-3.0-or-later",
5 | "peerDependencies": {
6 | "@angular/core": "^10.0.0"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/libs/core/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/libs/core",
4 | "lib": {
5 | "entryFile": "src/index.ts"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/apps/demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": ["node", "jest"],
5 | "resolveJsonModule": true
6 | },
7 | "include": ["**/*.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/libs/common/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/libs/common",
4 | "lib": {
5 | "entryFile": "src/index.ts"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/libs/utils/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/libs/utils",
4 | "lib": {
5 | "entryFile": "src/index.ts"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/apps/demo-e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "sourceMap": false,
5 | "outDir": "../../dist/out-tsc"
6 | },
7 | "include": ["src/**/*.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/libs/app/src/electron/src/services/index.ts:
--------------------------------------------------------------------------------
1 | import { ElectronService } from './electron.service';
2 |
3 | export const ELECTRON_PROVIDERS: any[] = [ElectronService];
4 |
5 | export * from './electron.service';
6 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "nrwl.angular-console",
4 | "angular.ng-template",
5 | "ms-vscode.vscode-typescript-tslint-plugin",
6 | "esbenp.prettier-vscode"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/libs/app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": [
5 | "node",
6 | "jest"
7 | ]
8 | },
9 | "include": [
10 | "**/*.ts"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/libs/cli/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": [
5 | "node",
6 | "jest"
7 | ]
8 | },
9 | "include": [
10 | "**/*.ts"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/libs/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": [
5 | "node",
6 | "jest"
7 | ]
8 | },
9 | "include": [
10 | "**/*.ts"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/libs/utils/src/lib/objects.ts:
--------------------------------------------------------------------------------
1 | export const isString = function(arg: any) {
2 | return typeof arg === 'string';
3 | };
4 |
5 | export const isObject = function(arg: any) {
6 | return arg && typeof arg === 'object';
7 | };
8 |
--------------------------------------------------------------------------------
/libs/app/src/web/src/core.module.spec.ts:
--------------------------------------------------------------------------------
1 | import { CoreModule } from './core.module';
2 |
3 | describe('LoclCoreModule', () => {
4 | it('should work', () => {
5 | expect(new CoreModule(null)).toBeDefined();
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/libs/common/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": [
5 | "node",
6 | "jest"
7 | ]
8 | },
9 | "include": [
10 | "**/*.ts"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/libs/core/src/lib/interfaces.ts:
--------------------------------------------------------------------------------
1 | import { ɵMessageId, ɵTargetMessage } from '@angular/localize';
2 |
3 | export interface ParsedTranslationBundle {
4 | locale: string;
5 | translations: Record<ɵMessageId, ɵTargetMessage>;
6 | }
7 |
--------------------------------------------------------------------------------
/libs/utils/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": [
5 | "node",
6 | "jest"
7 | ]
8 | },
9 | "include": [
10 | "**/*.ts"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/apps/web-e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "sourceMap": false,
5 | "outDir": "../../dist/out-tsc"
6 | },
7 | "include": [
8 | "src/**/*.ts"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/web-e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "types": [
5 | "cypress",
6 | "node"
7 | ]
8 | },
9 | "include": [
10 | "**/*.ts"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/testing/tsconfig.xplat.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.libs.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/out-tsc/xplat"
5 | },
6 | "exclude": [
7 | "test.xplat.ts",
8 | "**/*.spec.ts"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/libs/cli/src/lib/cli.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 |
3 | // not used, only there to make ng-packagr happy
4 | @NgModule({
5 | imports: [],
6 | declarations: [],
7 | exports: []
8 | })
9 | export class CliModule {}
10 |
--------------------------------------------------------------------------------
/libs/scss/_index.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * The following are shared across all platforms and apps
3 | */
4 | // shared variables
5 | @import 'variables';
6 | body {
7 | background-color: #fff;
8 | }
9 | // create/import other scss files or define as needed...
10 |
--------------------------------------------------------------------------------
/libs/utils/src/lib/angular.ts:
--------------------------------------------------------------------------------
1 | export function throwIfAlreadyLoaded(parentModule: any, moduleName: string) {
2 | if (parentModule) {
3 | throw new Error(
4 | `${moduleName} has already been loaded. Import ${moduleName} in the AppModule only.`
5 | );
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/apps/demo/src/assets/i18n/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "locale": "fr",
3 | "translations": {
4 | "143924624516046945": "Ça fonctionne! {$INTERPOLATION}",
5 | "6586379816467235622": "Bienvenue à la démo de {$PH} et {$PH_1} fait pour {$PH_2}!",
6 | "foo": "id personnalisé"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/apps/demo/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
{{ title }}!
2 |
3 | It works! {{ title }}
4 |
5 |
6 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/apps/web-e2e/src/integration/app.spec.ts:
--------------------------------------------------------------------------------
1 | import { getGreeting } from '../support/app.po';
2 |
3 | describe('web', () => {
4 | beforeEach(() => cy.visit('/'));
5 |
6 | it('should display welcome message', () => {
7 | getGreeting().contains('Welcome to web!');
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/apps/demo-e2e/src/integration/app.spec.ts:
--------------------------------------------------------------------------------
1 | import { getGreeting } from '../support/app.po';
2 |
3 | describe('web-demo', () => {
4 | beforeEach(() => cy.visit('/'));
5 |
6 | it('should display welcome message', () => {
7 | getGreeting().contains('Welcome to web-demo!');
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/apps/demo/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": ["jest", "node"]
7 | },
8 | "files": ["src/test-setup.ts"],
9 | "include": ["**/*.spec.ts", "**/*.d.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/apps/demo/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "locl", "camelCase"],
5 | "component-selector": [true, "element", "locl", "kebab-case"]
6 | },
7 | "linterOptions": {
8 | "exclude": ["!**/*"]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/apps/web/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "locl", "camelCase"],
5 | "component-selector": [true, "element", "locl", "kebab-case"]
6 | },
7 | "linterOptions": {
8 | "exclude": ["!**/*"]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
3 | transform: {
4 | '^.+\\.(ts|js|html)$': 'ts-jest'
5 | },
6 | resolver: '@nrwl/jest/plugins/resolver',
7 | moduleFileExtensions: ['ts', 'js', 'html'],
8 | coverageReporters: ['html']
9 | };
10 |
--------------------------------------------------------------------------------
/libs/app/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "locl", "camelCase"],
5 | "component-selector": [true, "element", "locl", "kebab-case"]
6 | },
7 | "linterOptions": {
8 | "exclude": ["!**/*"]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/libs/cli/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "locl", "camelCase"],
5 | "component-selector": [true, "element", "locl", "kebab-case"]
6 | },
7 | "linterOptions": {
8 | "exclude": ["!**/*"]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/libs/core/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "locl", "camelCase"],
5 | "component-selector": [true, "element", "locl", "kebab-case"]
6 | },
7 | "linterOptions": {
8 | "exclude": ["!**/*"]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/libs/utils/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "locl", "camelCase"],
5 | "component-selector": [true, "element", "locl", "kebab-case"]
6 | },
7 | "linterOptions": {
8 | "exclude": ["!**/*"]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/libs/common/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [true, "attribute", "locl", "camelCase"],
5 | "component-selector": [true, "element", "locl", "kebab-case"]
6 | },
7 | "linterOptions": {
8 | "exclude": ["!**/*"]
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/apps/web/src/assets/img/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #ffffff
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/libs/app/src/web/src/services/index.ts:
--------------------------------------------------------------------------------
1 | import { LogService } from './log.service';
2 | import { WindowService } from './window.service';
3 |
4 | export const CORE_PROVIDERS: any[] = [LogService, WindowService];
5 |
6 | export * from './log.service';
7 | export * from './window.service';
8 | export * from './tokens';
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = false
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/apps/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "types": []
6 | },
7 | "include": [
8 | "src/main.ts",
9 | "src/polyfills.ts"
10 | ],
11 | "exclude": [
12 | "src/test-setup.ts",
13 | "**/*.spec.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/tools/tsconfig.tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/out-tsc/tools",
5 | "rootDir": ".",
6 | "module": "commonjs",
7 | "target": "es5",
8 | "types": [
9 | "node"
10 | ]
11 | },
12 | "include": [
13 | "**/*.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/apps/demo/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "types": [],
6 | "resolveJsonModule": true
7 | },
8 | "files": ["src/main.ts", "src/polyfills.ts"],
9 | "include": ["**/*.ts"],
10 | "exclude": ["src/test-setup.ts", "**/*.spec.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/libs/cli/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/libs/cli",
4 | "lib": {
5 | "entryFile": "src/index.ts"
6 | },
7 | "whitelistedNonPeerDependencies": [
8 | "@babel/core",
9 | "chalk",
10 | "find-up",
11 | "glob",
12 | "yargs"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | '@commitlint/config-conventional',
4 | '@commitlint/config-lerna-scopes',
5 | ],
6 | plugins: ['commitlint-plugin-body-content'],
7 | rules: {
8 | 'body-content': [2, 'always', ['affects:', ['fix', 'feat', 'perf']]],
9 | 'footer-max-line-length': [0],
10 | },
11 | };
12 |
--------------------------------------------------------------------------------
/testing/tsconfig.libs.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/out-tsc/libs",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "test.libs.ts"
12 | ],
13 | "include": [
14 | "../libs/**/*.spec.ts",
15 | "../libs/**/*.d.ts"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/apps/demo/src/app/lazy-loaded/lazy-loaded.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'locl-lazy-loaded',
5 | template: `
6 | Lazy loading works!
7 | `,
8 | styles: []
9 | })
10 | export class LazyLoadedComponent implements OnInit {
11 | constructor() {}
12 |
13 | ngOnInit() {}
14 | }
15 |
--------------------------------------------------------------------------------
/apps/web/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": [
7 | "jest",
8 | "node"
9 | ]
10 | },
11 | "files": [
12 | "src/test-setup.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/libs/app/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": [
7 | "jest",
8 | "node"
9 | ]
10 | },
11 | "files": [
12 | "src/test-setup.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/libs/cli/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": [
7 | "jest",
8 | "node"
9 | ]
10 | },
11 | "files": [
12 | "src/test-setup.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/libs/common/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": [
7 | "jest",
8 | "node"
9 | ]
10 | },
11 | "files": [
12 | "src/test-setup.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/libs/core/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": [
7 | "jest",
8 | "node"
9 | ]
10 | },
11 | "files": [
12 | "src/test-setup.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/libs/utils/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "module": "commonjs",
6 | "types": [
7 | "jest",
8 | "node"
9 | ]
10 | },
11 | "files": [
12 | "src/test-setup.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/testing/tsconfig.xplat.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.libs.spec.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/out-tsc/xplat"
5 | },
6 | "files": [
7 | "test.xplat.ts"
8 | ],
9 | "include": [
10 | "../libs/**/*.spec.ts",
11 | "../libs/**/*.d.ts",
12 | "../xplat/web/**/*.spec.ts",
13 | "../xplat/web/**/*.d.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/libs/app/src/web/scss/_index.scss:
--------------------------------------------------------------------------------
1 | // shared across all platforms and apps
2 | @import '../../../../../node_modules/@locl/scss/index';
3 | /**
4 | * The following are web specific (used with any web app targets)
5 | */
6 | // web specific variables
7 | @import 'variables';
8 | // web styles (create/import other scss files or define as needed)
9 | @import 'spacing';
10 | @import 'tags';
11 |
--------------------------------------------------------------------------------
/apps/demo/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | WebDemo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/web/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'web',
3 | preset: '../../jest.config.js',
4 | coverageDirectory: '../../coverage/apps/web',
5 | snapshotSerializers: [
6 | 'jest-preset-angular/build/AngularNoNgAttributesSnapshotSerializer.js',
7 | 'jest-preset-angular/build/AngularSnapshotSerializer.js',
8 | 'jest-preset-angular/build/HTMLCommentSerializer.js'
9 | ]
10 | };
11 |
--------------------------------------------------------------------------------
/apps/web/src/app/app.electron.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { LoclElectronCoreModule } from '@locl/app-electron';
3 | import { AppComponent } from './app.component';
4 | import { AppModule } from './app.module';
5 |
6 | @NgModule({
7 | imports: [AppModule, LoclElectronCoreModule],
8 | bootstrap: [AppComponent]
9 | })
10 | export class AppElectronModule {
11 | }
12 |
--------------------------------------------------------------------------------
/apps/demo/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'web-demo',
3 | preset: '../../jest.config.js',
4 | coverageDirectory: '../../coverage/apps/web-demo',
5 | snapshotSerializers: [
6 | 'jest-preset-angular/build/AngularNoNgAttributesSnapshotSerializer.js',
7 | 'jest-preset-angular/build/AngularSnapshotSerializer.js',
8 | 'jest-preset-angular/build/HTMLCommentSerializer.js'
9 | ]
10 | };
11 |
--------------------------------------------------------------------------------
/apps/web-e2e/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileServerFolder": ".",
3 | "fixturesFolder": "./src/fixtures",
4 | "integrationFolder": "./src/integration",
5 | "pluginsFile": "./src/plugins/index",
6 | "supportFile": false,
7 | "video": true,
8 | "videosFolder": "../../dist/cypress/apps/web-e2e/videos",
9 | "screenshotsFolder": "../../dist/cypress/apps/web-e2e/screenshots",
10 | "chromeWebSecurity": false
11 | }
12 |
--------------------------------------------------------------------------------
/testing/test.xplat.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Test all shared code across /libs and /xplat
3 | * Split out to allow targeted testing against /libs or various xplat support folders
4 | */
5 | import './test.libs';
6 |
7 | declare const require: any;
8 |
9 | // web support only right now
10 | const contextWeb = require.context('../xplat/web', true, /\.spec\.ts$/);
11 | // And load the modules.
12 | contextWeb.keys().map(contextWeb);
13 |
--------------------------------------------------------------------------------
/apps/demo-e2e/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileServerFolder": ".",
3 | "fixturesFolder": "./src/fixtures",
4 | "integrationFolder": "./src/integration",
5 | "pluginsFile": "./src/plugins/index",
6 | "supportFile": false,
7 | "video": true,
8 | "videosFolder": "../../dist/cypress/apps/web-demo-e2e/videos",
9 | "screenshotsFolder": "../../dist/cypress/apps/web-demo-e2e/screenshots",
10 | "chromeWebSecurity": false
11 | }
12 |
--------------------------------------------------------------------------------
/apps/web/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { BrowserModule } from '@angular/platform-browser';
3 | import { CoreModule } from '@locl/app-web';
4 |
5 | import { AppComponent } from './app.component';
6 |
7 | @NgModule({
8 | declarations: [AppComponent],
9 | imports: [CoreModule, BrowserModule],
10 | providers: [],
11 | bootstrap: [AppComponent]
12 | })
13 | export class AppModule {
14 | }
15 |
--------------------------------------------------------------------------------
/libs/app/src/web/src/services/tokens.ts:
--------------------------------------------------------------------------------
1 | import { InjectionToken } from '@angular/core';
2 |
3 | /**
4 | * Various InjectionTokens shared across all platforms
5 | * Always suffix with 'Token' for clarity and consistency
6 | */
7 |
8 | export const PlatformLanguageToken = new InjectionToken(
9 | 'PlatformLanguage'
10 | );
11 |
12 | export const PlatformWindowToken = new InjectionToken(
13 | 'PlatformWindow'
14 | );
15 |
--------------------------------------------------------------------------------
/apps/web/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'locl-root',
5 | styleUrls: ['./app.component.scss'],
6 | template: `
7 |
8 |
Welcome to {{ title }}!
9 |
10 |
11 |
12 | `
13 | })
14 | export class AppComponent {
15 | title = 'web';
16 | }
17 |
--------------------------------------------------------------------------------
/apps/web/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic()
12 | .bootstrapModule(AppModule)
13 | .catch(err => console.error(err));
14 |
--------------------------------------------------------------------------------
/apps/demo/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { BrowserModule } from '@angular/platform-browser';
3 | import { AppRoutingModule } from './app-routing.module';
4 |
5 | import { AppComponent } from './app.component';
6 |
7 | @NgModule({
8 | imports: [BrowserModule, AppRoutingModule],
9 | providers: [],
10 | declarations: [AppComponent],
11 | bootstrap: [AppComponent]
12 | })
13 | export class AppModule {}
14 |
--------------------------------------------------------------------------------
/libs/common/src/lib/common.module.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, TestBed } from '@angular/core/testing';
2 | import { LoclCommonModule } from './common.module';
3 |
4 | describe('CommonModule', () => {
5 | beforeEach(async(() => {
6 | TestBed.configureTestingModule({
7 | imports: [LoclCommonModule]
8 | }).compileComponents();
9 | }));
10 |
11 | it('should create', () => {
12 | expect(LoclCommonModule).toBeDefined();
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/apps/web/src/main.electron.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppElectronModule } from './app/app.electron.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic()
12 | .bootstrapModule(AppElectronModule)
13 | .catch(err => console.log(err));
14 |
--------------------------------------------------------------------------------
/apps/demo/browserslist:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # You can see what browsers were selected by your queries by running:
6 | # npx browserslist
7 |
8 | > 0.5%
9 | last 2 versions
10 | Firefox ESR
11 | not dead
12 | not IE 9-11 # For IE 9-11 support, remove 'not'.
--------------------------------------------------------------------------------
/apps/web/browserslist:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # You can see what browsers were selected by your queries by running:
6 | # npx browserslist
7 |
8 | > 0.5%
9 | last 2 versions
10 | Firefox ESR
11 | not dead
12 | not IE 9-11 # For IE 9-11 support, remove 'not'.
--------------------------------------------------------------------------------
/apps/demo/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | const name = '$localize';
4 | const lib = 'Locl';
5 |
6 | @Component({
7 | selector: 'locl-root',
8 | styleUrls: ['./app.component.scss'],
9 | templateUrl: './app.component.html'
10 | })
11 | export class AppComponent {
12 | title = $localize`Welcome to the demo of ${name} and ${lib} made for ${name}!`;
13 |
14 | constructor() {
15 | console.log($localize`:@@foo:custom id!`);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/libs/cli/src/main.ts:
--------------------------------------------------------------------------------
1 | import * as yargs from 'yargs';
2 | import * as findUp from 'find-up';
3 | import * as fs from 'fs';
4 | const configPath = findUp.sync(['.loclrc', '.locl.json']);
5 | const config = configPath
6 | ? JSON.parse(fs.readFileSync(configPath, { encoding: 'utf8' }))
7 | : {};
8 |
9 | const args = process.argv.slice(2);
10 | yargs
11 | .config(config)
12 | .commandDir('cmds')
13 | .demandCommand()
14 | .strict()
15 | .help()
16 | .version(false)
17 | .parse(args);
18 |
--------------------------------------------------------------------------------
/tools/commitlint-plugin-body-content/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "commitlint-plugin-body-content",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "commitlint-plugin-body-content.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [
10 | "commitlint",
11 | "commitlintplugin"
12 | ],
13 | "author": "",
14 | "license": "ISC",
15 | "peerDependencies": {
16 | "@commitlint/lint": ">=7.6.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/apps/electron/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "sourceMap": true,
4 | "declaration": false,
5 | "moduleResolution": "node",
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "esModuleInterop": true,
9 | "target": "es5",
10 | "typeRoots": [
11 | "../../node_modules/@types"
12 | ],
13 | "lib": [
14 | "es2017",
15 | "es2016",
16 | "es2015",
17 | "dom"
18 | ]
19 | },
20 | "include": [
21 | "src/index.ts"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/apps/demo/src/app/lazy-loaded/lazy-loaded.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { RouterModule, Routes } from '@angular/router';
4 | import { LazyLoadedComponent } from './lazy-loaded.component';
5 |
6 | const lazyRoutes: Routes = [
7 | {
8 | path: '',
9 | component: LazyLoadedComponent
10 | }
11 | ];
12 |
13 | @NgModule({
14 | imports: [CommonModule, RouterModule.forChild(lazyRoutes)],
15 | declarations: [LazyLoadedComponent]
16 | })
17 | export class LazyLoadedModule {}
18 |
--------------------------------------------------------------------------------
/apps/web/src/assets/img/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "short_name": "",
4 | "icons": [
5 | {
6 | "src": "/assets/img/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/assets/img/android-chrome-256x256.png",
12 | "sizes": "256x256",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/apps/demo/src/i18n-setup.ts:
--------------------------------------------------------------------------------
1 | // import { computeMsgId } from '@angular/compiler';
2 | // import { loadTranslations } from '@angular/localize';
3 |
4 | /***************************************************************************************************
5 | * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
6 | */
7 | import '@angular/localize/init';
8 | // import * as fr from './assets/i18n/fr.json';
9 | // loadTranslations((fr as any).default.translations);
10 | // console.log('hey',computeMsgId('Welcome to {$INTERPOLATION}!', ''));
11 |
--------------------------------------------------------------------------------
/libs/app/src/electron/src/core.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule, Optional, SkipSelf } from '@angular/core';
2 |
3 | import { throwIfAlreadyLoaded } from '@locl/utils';
4 | import { ELECTRON_PROVIDERS, ElectronService } from './services';
5 |
6 | @NgModule({
7 | providers: [...ELECTRON_PROVIDERS]
8 | })
9 | export class LoclElectronCoreModule {
10 | constructor(
11 | @Optional()
12 | @SkipSelf()
13 | parentModule: LoclElectronCoreModule,
14 | private _electronService: ElectronService
15 | ) {
16 | throwIfAlreadyLoaded(parentModule, 'LoclElectronCoreModule');
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/libs/cli/src/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.lib.json",
3 | "compilerOptions": {
4 | "outDir": "../../../dist",
5 | "lib": ["es2017", "dom"],
6 | "module": "commonjs",
7 | "moduleResolution": "node",
8 | "noEmitOnError": true,
9 | "noFallthroughCasesInSwitch": true,
10 | "noImplicitAny": false,
11 | "noImplicitThis": true,
12 | "noUnusedParameters": false,
13 | "noUnusedLocals": false,
14 | "skipDefaultLibCheck": true,
15 | "skipLibCheck": true,
16 | "strictNullChecks": false,
17 | "target": "es6"
18 | },
19 | "include": ["**/*"],
20 | "exclude": []
21 | }
22 |
--------------------------------------------------------------------------------
/apps/demo/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 | import { getTranslations, ParsedTranslationBundle } from '@locl/core';
4 | import { AppModule } from './app/app.module';
5 |
6 | import { environment } from './environments/environment';
7 |
8 | if (environment.production) {
9 | enableProdMode();
10 | }
11 |
12 | getTranslations('/assets/i18n/fr.json').then(
13 | (data: ParsedTranslationBundle) => {
14 | platformBrowserDynamic()
15 | .bootstrapModule(AppModule)
16 | .catch(err => console.error(err));
17 | }
18 | );
19 |
--------------------------------------------------------------------------------
/libs/app/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": [
10 | "dom",
11 | "es2018"
12 | ]
13 | },
14 | "angularCompilerOptions": {
15 | "annotateForClosureCompiler": true,
16 | "skipTemplateCodegen": true,
17 | "strictMetadataEmit": true,
18 | "fullTemplateTypeCheck": true,
19 | "strictInjectionParameters": true,
20 | "enableResourceInlining": true
21 | },
22 | "exclude": [
23 | "src/test.ts",
24 | "**/*.spec.ts"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/testing/test.libs.ts:
--------------------------------------------------------------------------------
1 | import { getTestBed } from '@angular/core/testing';
2 | import {
3 | BrowserDynamicTestingModule,
4 | platformBrowserDynamicTesting
5 | } from '@angular/platform-browser-dynamic/testing';
6 | import 'zone.js/dist/zone';
7 | import 'zone.js/dist/zone-testing';
8 |
9 | declare const require: any;
10 |
11 | // First, initialize the Angular testing environment.
12 | getTestBed().initTestEnvironment(
13 | BrowserDynamicTestingModule,
14 | platformBrowserDynamicTesting()
15 | );
16 | // Then we find all the tests.
17 | const contextLibs = require.context('../libs', true, /\.spec\.ts$/);
18 | // And load the modules.
19 | contextLibs.keys().map(contextLibs);
20 |
--------------------------------------------------------------------------------
/apps/web-e2e/src/support/index.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [ocombe]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/apps/demo-e2e/src/support/index.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
18 |
--------------------------------------------------------------------------------
/libs/cli/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": [
10 | "dom",
11 | "es2018"
12 | ]
13 | },
14 | "angularCompilerOptions": {
15 | "annotateForClosureCompiler": true,
16 | "skipTemplateCodegen": true,
17 | "strictMetadataEmit": true,
18 | "fullTemplateTypeCheck": true,
19 | "strictInjectionParameters": true,
20 | "enableResourceInlining": true,
21 | "enableIvy": false
22 | },
23 | "exclude": [
24 | "src/test.ts",
25 | "**/*.spec.ts"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/libs/core/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": [
10 | "dom",
11 | "es2018"
12 | ]
13 | },
14 | "angularCompilerOptions": {
15 | "annotateForClosureCompiler": true,
16 | "skipTemplateCodegen": true,
17 | "strictMetadataEmit": true,
18 | "fullTemplateTypeCheck": true,
19 | "strictInjectionParameters": true,
20 | "enableResourceInlining": true,
21 | "enableIvy": false
22 | },
23 | "exclude": [
24 | "src/test.ts",
25 | "**/*.spec.ts"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/libs/common/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": [
10 | "dom",
11 | "es2018"
12 | ]
13 | },
14 | "angularCompilerOptions": {
15 | "annotateForClosureCompiler": true,
16 | "skipTemplateCodegen": true,
17 | "strictMetadataEmit": true,
18 | "fullTemplateTypeCheck": true,
19 | "strictInjectionParameters": true,
20 | "enableResourceInlining": true,
21 | "enableIvy": false
22 | },
23 | "exclude": [
24 | "src/test.ts",
25 | "**/*.spec.ts"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/libs/utils/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist/out-tsc",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": [
10 | "dom",
11 | "es2018"
12 | ]
13 | },
14 | "angularCompilerOptions": {
15 | "annotateForClosureCompiler": true,
16 | "skipTemplateCodegen": true,
17 | "strictMetadataEmit": true,
18 | "fullTemplateTypeCheck": true,
19 | "strictInjectionParameters": true,
20 | "enableResourceInlining": true,
21 | "enableIvy": false
22 | },
23 | "exclude": [
24 | "src/test.ts",
25 | "**/*.spec.ts"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/apps/web/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/tools/web/postinstall.js:
--------------------------------------------------------------------------------
1 | // ALL CREDIT TO: Maxime GRIS https://github.com/maximegris
2 | // Allow angular using electron module (native node modules)
3 | const fs = require('fs');
4 | const f_angular =
5 | 'node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/browser.js';
6 |
7 | fs.readFile(f_angular, 'utf8', function(err, data) {
8 | if (err) {
9 | return console.log(err);
10 | }
11 | var result = data.replace(/target: "electron-renderer",/g, '');
12 | var result = result.replace(/target: "web",/g, '');
13 | var result = result.replace(/return \{/g, 'return {target: "web",');
14 |
15 | fs.writeFile(f_angular, result, 'utf8', function(err) {
16 | if (err) return console.log(err);
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/apps/demo/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/tools/electron/postinstall.js:
--------------------------------------------------------------------------------
1 | // ALL CREDIT TO: Maxime GRIS https://github.com/maximegris
2 | // Allow angular using electron module (native node modules)
3 | const fs = require('fs');
4 | const f_angular =
5 | 'node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/browser.js';
6 |
7 | fs.readFile(f_angular, 'utf8', function(err, data) {
8 | if (err) {
9 | return console.log(err);
10 | }
11 | var result = data.replace(/target: "electron-renderer",/g, '');
12 | var result = result.replace(/target: "web",/g, '');
13 | var result = result.replace(
14 | /return \{/g,
15 | 'return {target: "electron-renderer",'
16 | );
17 |
18 | fs.writeFile(f_angular, result, 'utf8', function(err) {
19 | if (err) return console.log(err);
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/extract/translation_files/translation_serializer.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 | import { ɵParsedMessage } from '@angular/localize';
9 |
10 | /**
11 | * Implement this interface to provide a class that can render messages into a translation file.
12 | */
13 | export interface TranslationSerializer {
14 | /**
15 | * Render the contents of a translation file containing the given `messages`.
16 | * @param messages The messages to render to the file.
17 | */
18 | renderFile(
19 | messages: ɵParsedMessage[],
20 | locale: string,
21 | isTarget?: boolean
22 | ): string;
23 | }
24 |
--------------------------------------------------------------------------------
/apps/demo/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { RouterModule, Routes } from '@angular/router';
4 | import { getTranslations, ParsedTranslationBundle } from '@locl/core';
5 |
6 | const routes: Routes = [
7 | {
8 | path: 'lazy',
9 | loadChildren: () =>
10 | getTranslations('/assets/i18n/lazy.fr.json').then(
11 | (data: ParsedTranslationBundle) => {
12 | return import('./lazy-loaded/lazy-loaded.module').then(
13 | mod => mod.LazyLoadedModule
14 | );
15 | }
16 | )
17 | }
18 | ];
19 |
20 | @NgModule({
21 | declarations: [],
22 | imports: [CommonModule, RouterModule.forRoot(routes)],
23 | exports: [RouterModule]
24 | })
25 | export class AppRoutingModule {}
26 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/convert/translations.ts:
--------------------------------------------------------------------------------
1 | import { ɵMessageId, ɵParsedTranslation } from '@angular/localize';
2 |
3 | /**
4 | * A translation message that has been processed to extract the message parts and placeholders.
5 | */
6 | export interface ParsedTranslation extends ɵParsedTranslation {
7 | messageParts: TemplateStringsArray;
8 | placeholderNames: string[];
9 | description?: string;
10 | meaning?: string;
11 | }
12 |
13 | /**
14 | * The internal structure used by the runtime localization to translate messages.
15 | */
16 | export declare type ParsedTranslations = Record<ɵMessageId, ParsedTranslation>;
17 |
18 | /**
19 | * An object that holds translations that have been parsed from a translation file.
20 | */
21 | export interface ParsedTranslationBundle {
22 | locale: string | undefined;
23 | translations: ParsedTranslations;
24 | }
25 |
--------------------------------------------------------------------------------
/libs/utils/src/lib/platform.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * NativeScript helpers
3 | */
4 |
5 | declare var NSObject: any, NSString: any, android: any, java: any, window: any;
6 |
7 | /**
8 | * Determine if running on native iOS mobile app
9 | */
10 | export function isIOS() {
11 | return typeof NSObject !== 'undefined' && typeof NSString !== 'undefined';
12 | }
13 |
14 | /**
15 | * Determine if running on native Android mobile app
16 | */
17 | export function isAndroid() {
18 | return typeof android !== 'undefined' && typeof java !== 'undefined';
19 | }
20 |
21 | /**
22 | * Determine if running on native iOS or Android mobile app
23 | */
24 | export function isNativeScript() {
25 | return isIOS() || isAndroid();
26 | }
27 |
28 | /**
29 | * Electron helpers
30 | */
31 | export function isElectron() {
32 | return typeof window !== 'undefined' && window.process && window.process.type;
33 | }
34 |
--------------------------------------------------------------------------------
/apps/demo-e2e/src/plugins/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example plugins/index.js can be used to load plugins
3 | //
4 | // You can change the location of this file or turn off loading
5 | // the plugins file with the 'pluginsFile' configuration option.
6 | //
7 | // You can read more here:
8 | // https://on.cypress.io/plugins-guide
9 | // ***********************************************************
10 |
11 | // This function is called when a project is opened or re-opened (e.g. due to
12 | // the project's config changing)
13 |
14 | const { preprocessTypescript } = require('@nrwl/cypress/plugins/preprocessor');
15 |
16 | module.exports = (on, config) => {
17 | // `on` is used to hook into various events Cypress emits
18 | // `config` is the resolved Cypress config
19 |
20 | // Preprocess Typescript
21 | on('file:preprocessor', preprocessTypescript(config));
22 | };
23 |
--------------------------------------------------------------------------------
/apps/web-e2e/src/plugins/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example plugins/index.js can be used to load plugins
3 | //
4 | // You can change the location of this file or turn off loading
5 | // the plugins file with the 'pluginsFile' configuration option.
6 | //
7 | // You can read more here:
8 | // https://on.cypress.io/plugins-guide
9 | // ***********************************************************
10 |
11 | // This function is called when a project is opened or re-opened (e.g. due to
12 | // the project's config changing)
13 |
14 | const { preprocessTypescript } = require('@nrwl/cypress/plugins/preprocessor');
15 |
16 | module.exports = (on, config) => {
17 | // `on` is used to hook into various events Cypress emits
18 | // `config` is the resolved Cypress config
19 |
20 | // Preprocess Typescript
21 | on('file:preprocessor', preprocessTypescript(config));
22 | };
23 |
--------------------------------------------------------------------------------
/testing/tsconfig.libs.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/out-tsc/libs",
5 | "target": "es2015",
6 | "module": "es2015",
7 | "moduleResolution": "node",
8 | "declaration": true,
9 | "sourceMap": true,
10 | "inlineSources": true,
11 | "emitDecoratorMetadata": true,
12 | "experimentalDecorators": true,
13 | "importHelpers": true,
14 | "types": [],
15 | "lib": [
16 | "dom",
17 | "es2015"
18 | ]
19 | },
20 | "angularCompilerOptions": {
21 | "annotateForClosureCompiler": true,
22 | "skipTemplateCodegen": true,
23 | "strictMetadataEmit": true,
24 | "fullTemplateTypeCheck": true,
25 | "strictInjectionParameters": true,
26 | "flatModuleId": "AUTOGENERATED",
27 | "flatModuleOutFile": "AUTOGENERATED"
28 | },
29 | "exclude": [
30 | "test.libs.ts",
31 | "**/*.spec.ts"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/convert/message_serialization/message_renderer.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 |
9 | export interface MessageRenderer {
10 | message: T;
11 | startRender(): void;
12 | endRender(): void;
13 | text(text: string): void;
14 | description(text: string): void;
15 | meaning(text: string): void;
16 | placeholder(name: string, body: string | undefined): void;
17 | startPlaceholder(name: string): void;
18 | closePlaceholder(name: string): void;
19 | startContainer(): void;
20 | closeContainer(): void;
21 | startIcu(): void;
22 | endIcu(): void;
23 | }
24 |
25 | export function stripInterpolationMarkers(interpolation: string): string {
26 | return interpolation.replace(/^\{\{/, '').replace(/}}$/, '');
27 | }
28 |
--------------------------------------------------------------------------------
/apps/demo-e2e/src/support/commands.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add("login", (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This will overwrite an existing command --
25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
26 |
--------------------------------------------------------------------------------
/apps/web-e2e/src/support/commands.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add("login", (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This will overwrite an existing command --
25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
26 |
--------------------------------------------------------------------------------
/libs/scss/_variables.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Shared across all platforms and apps
3 | * You may define a set of global variables accessible across entire workspace here
4 | */
5 |
6 | // could use a base theme here, for example:
7 | $radius: 2px;
8 |
9 | /* Colors: https://coolors.co/ffc338-ba2727-0874bd-373734-f0f4ed */
10 | $red: #ba2727;
11 | $yellow: #ffc338;
12 | $orange: #ffa814;
13 | $green: #83b800;
14 | $blue: #0874bd;
15 | $cyan: #209CEE;
16 | $black: #1c1c1c;
17 | $grey-darker: #373a4c;
18 | $grey-dark: #373734;
19 | $grey-light: #f0f4ed;
20 | $beige-light: #D0D1CD;
21 | $beige-lighter: #EFF0EB;
22 |
23 | $primary: $blue;
24 | $secondary: $green;
25 | $background: $grey-dark;
26 | $content-bg: #fff;
27 | $btn-bg: $grey-dark;
28 | $btn-color: #fff;
29 |
30 | $text-color: $black;
31 | $text: $grey-dark;
32 | $link: $blue;
33 | $info: $cyan;
34 | $success: $green;
35 | $warning: $yellow;
36 | $danger: $red;
37 | $dark: $grey-darker;
38 |
39 | $input-background-color: $beige-lighter;
40 | $input-shadow: none;
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | yarn-error.log
34 | testem.log
35 | /typings
36 | .released-packages
37 |
38 | # System Files
39 | .DS_Store
40 | Thumbs.db
41 |
42 | # libs
43 | libs/**/*.js
44 | libs/**/*.map
45 | libs/**/*.d.ts
46 | libs/**/*.metadata.json
47 | libs/**/*.ngfactory.ts
48 | libs/**/*.ngsummary.json
49 |
50 | # xplat
51 | xplat/**/*.js
52 | xplat/**/*.map
53 | xplat/**/*.d.ts
54 | xplat/**/*.metadata.json
55 | xplat/**/*.ngfactory.ts
56 | xplat/**/*.ngsummary.json
57 |
--------------------------------------------------------------------------------
/nx.json:
--------------------------------------------------------------------------------
1 | {
2 | "npmScope": "locl",
3 | "implicitDependencies": {
4 | "angular.json": "*",
5 | "package.json": "*",
6 | "tsconfig.json": "*",
7 | "tslint.json": "*",
8 | "nx.json": "*"
9 | },
10 | "projects": {
11 | "web-e2e": {
12 | "tags": []
13 | },
14 | "web": {
15 | "tags": ["web"]
16 | },
17 | "electron": {
18 | "tags": ["electron"]
19 | },
20 | "core": {
21 | "tags": ["core"]
22 | },
23 | "common": {
24 | "tags": ["common"]
25 | },
26 | "utils": {
27 | "tags": ["utils"]
28 | },
29 | "cli": {
30 | "tags": ["cli"]
31 | },
32 | "app": {
33 | "tags": ["app:web", "app:electron"]
34 | },
35 | "demo-e2e": {
36 | "tags": []
37 | },
38 | "demo": {
39 | "tags": []
40 | }
41 | },
42 | "tasksRunnerOptions": {
43 | "default": {
44 | "runner": "@nrwl/workspace/tasks-runners/default",
45 | "options": {
46 | "cacheableOperations": ["build", "lint", "test", "e2e"]
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/testing/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function(config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../coverage'),
20 | reports: ['html', 'lcovonly'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false
30 | });
31 | };
32 |
--------------------------------------------------------------------------------
/libs/core/src/lib/global.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 |
9 | // **********************************************************************************************
10 | // This code to access the global object is mostly copied from `packages/core/src/util/global.ts`
11 |
12 | declare global {
13 | var WorkerGlobalScope: any;
14 | }
15 |
16 | const __globalThis = typeof globalThis !== 'undefined' && globalThis;
17 | const __window = typeof window !== 'undefined' && window;
18 | const __self =
19 | typeof self !== 'undefined' &&
20 | typeof WorkerGlobalScope !== 'undefined' &&
21 | self instanceof WorkerGlobalScope &&
22 | self;
23 | // Always use __globalThis if available; this is the spec-defined global variable across all
24 | // environments.
25 | // Then fallback to __global first; in Node tests both __global and __window may be defined.
26 | export const _global: any = __globalThis || __window || __self;
27 |
--------------------------------------------------------------------------------
/apps/web/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, TestBed } from '@angular/core/testing';
2 | import { AppComponent } from './app.component';
3 |
4 | describe('AppComponent', () => {
5 | beforeEach(async(() => {
6 | TestBed.configureTestingModule({
7 | declarations: [AppComponent]
8 | }).compileComponents();
9 | }));
10 |
11 | it('should create the app', () => {
12 | const fixture = TestBed.createComponent(AppComponent);
13 | const app = fixture.debugElement.componentInstance;
14 | expect(app).toBeTruthy();
15 | });
16 |
17 | it(`should have as title 'web'`, () => {
18 | const fixture = TestBed.createComponent(AppComponent);
19 | const app = fixture.debugElement.componentInstance;
20 | expect(app.title).toEqual('web');
21 | });
22 |
23 | it('should render title in a h1 tag', () => {
24 | const fixture = TestBed.createComponent(AppComponent);
25 | fixture.detectChanges();
26 | const compiled = fixture.debugElement.nativeElement;
27 | expect(compiled.querySelector('h1').textContent).toContain(
28 | 'Welcome to web!'
29 | );
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/apps/demo/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 | import { AppComponent } from './app.component';
3 |
4 | describe('AppComponent', () => {
5 | beforeEach(async(() => {
6 | TestBed.configureTestingModule({
7 | declarations: [AppComponent]
8 | }).compileComponents();
9 | }));
10 |
11 | it('should create the app', () => {
12 | const fixture = TestBed.createComponent(AppComponent);
13 | const app = fixture.debugElement.componentInstance;
14 | expect(app).toBeTruthy();
15 | });
16 |
17 | it(`should have as title 'web-demo'`, () => {
18 | const fixture = TestBed.createComponent(AppComponent);
19 | const app = fixture.debugElement.componentInstance;
20 | expect(app.title).toEqual('web-demo');
21 | });
22 |
23 | it('should render title', () => {
24 | const fixture = TestBed.createComponent(AppComponent);
25 | fixture.detectChanges();
26 | const compiled = fixture.debugElement.nativeElement;
27 | expect(compiled.querySelector('h1').textContent).toContain(
28 | 'Welcome to web-demo!'
29 | );
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/convert/translation_parsers/translation_parse_error.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 | import { ParseErrorLevel, ParseSourceSpan } from '@angular/compiler';
9 |
10 | /**
11 | * This error is thrown when there is a problem parsing a translation file.
12 | */
13 | export class TranslationParseError extends Error {
14 | constructor(
15 | public span: ParseSourceSpan,
16 | public msg: string,
17 | public level: ParseErrorLevel = ParseErrorLevel.ERROR
18 | ) {
19 | super(msg);
20 | }
21 |
22 | contextualMessage(): string {
23 | const ctx = this.span.start.getContext(100, 3);
24 | return ctx
25 | ? `${this.msg} ("${ctx.before}[${ParseErrorLevel[this.level]} ->]${
26 | ctx.after
27 | }")`
28 | : this.msg;
29 | }
30 |
31 | toString(): string {
32 | const details = this.span.details ? `, ${this.span.details}` : '';
33 | return `${this.contextualMessage()}: ${this.span.start}${details}`;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/libs/app/src/electron/src/services/electron.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { LogService, WindowService } from '@locl/app-web';
3 | import { isElectron } from '@locl/utils';
4 | import * as childProcess from 'child_process';
5 | import { ipcRenderer, IpcRendererEvent } from 'electron';
6 |
7 | @Injectable()
8 | export class ElectronService {
9 | private _ipc: typeof ipcRenderer;
10 | private _childProcess: typeof childProcess;
11 |
12 | constructor(private _log: LogService, private _win: WindowService) {
13 | // Conditional imports
14 | if (isElectron()) {
15 | this._ipc = this._win.require('electron').ipcRenderer;
16 | this._childProcess = this._win.require('child_process');
17 | this._log.debug('ElectronService ready.');
18 | }
19 | }
20 |
21 | public on(channel: string, listener: (event: IpcRendererEvent, ...args: any[]) => void): void {
22 | if (!this._ipc) {
23 | return;
24 | }
25 |
26 | this._ipc.on(channel, listener);
27 | }
28 |
29 | public send(channel: string, ...args): void {
30 | if (!this._ipc) {
31 | return;
32 | }
33 |
34 | this._ipc.send(channel, ...args);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/libs/cli/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@locl/cli",
3 | "version": "1.0.0",
4 | "license": "AGPL-3.0-or-later",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/loclapp/locl/"
8 | },
9 | "bugs": {
10 | "url": "https://github.com/loclapp/locl/issues"
11 | },
12 | "homepage": "https://www.locl.app/",
13 | "bin": {
14 | "locl": "./src/locl"
15 | },
16 | "scripts": {
17 | "build": "ng build cli && npm run build:apiAndLib && npm run build:copy",
18 | "build:apiAndLib": "tsc -p src/tsconfig.json",
19 | "build:copy": "cpx src/locl ../../dist/libs/cli/src",
20 | "pretest": "npm run build",
21 | "test": "jasmine ../../dist/libs/cli/**/*spec.js"
22 | },
23 | "dependencies": {
24 | "@babel/core": "^7.8.6",
25 | "chalk": "^4.1.0",
26 | "find-up": "^4.1.0",
27 | "glob": "^7.1.2",
28 | "yargs": "^13.1.0"
29 | },
30 | "devDependencies": {
31 | "@types/babel__core": "^7.1.6",
32 | "@types/yargs": "^13.0.3",
33 | "@types/glob": "^7.1.1"
34 | },
35 | "peerDependencies": {
36 | "@angular/localize": "^10.0.0",
37 | "@angular/compiler": "^10.0.0",
38 | "@angular/core": "^10.0.0"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "rootDir": ".",
5 | "sourceMap": true,
6 | "declaration": false,
7 | "moduleResolution": "node",
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "importHelpers": true,
11 | "target": "es2015",
12 | "module": "esnext",
13 | "typeRoots": [
14 | "node_modules/@types"
15 | ],
16 | "lib": [
17 | "es2017",
18 | "dom"
19 | ],
20 | "skipLibCheck": true,
21 | "skipDefaultLibCheck": true,
22 | "baseUrl": ".",
23 | "paths": {
24 | "@locl/*": [
25 | "libs/*"
26 | ],
27 | "@locl/core": [
28 | "libs/core/src/index.ts"
29 | ],
30 | "@locl/utils": [
31 | "libs/utils/src/index.ts"
32 | ],
33 | "@locl/common": [
34 | "libs/common/src/index.ts"
35 | ],
36 | "@locl/cli": [
37 | "libs/cli/src/index.ts"
38 | ],
39 | "@locl/app-web": [
40 | "libs/app/src/web/index.ts"
41 | ],
42 | "@locl/app-electron": [
43 | "libs/app/src/electron/index.ts"
44 | ]
45 | }
46 | },
47 | "exclude": [
48 | "node_modules",
49 | "tmp"
50 | ]
51 | }
52 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/extract/translation_files/json_translation_serializer.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 | import { ɵParsedMessage } from '@angular/localize';
9 | import { ParsedMessageLegacy } from '../../../cmds/common/util';
10 | import { TranslationSerializer } from './translation_serializer';
11 |
12 | interface SimpleJsonTranslationFile {
13 | locale: string;
14 | translations: Record;
15 | }
16 |
17 | export class JsonTranslationSerializer implements TranslationSerializer {
18 | renderFile(
19 | messages: (ɵParsedMessage | ParsedMessageLegacy)[],
20 | locale: string
21 | ): string {
22 | const fileObj: SimpleJsonTranslationFile = {
23 | locale,
24 | translations: {}
25 | };
26 | messages.forEach(message => {
27 | fileObj.translations[
28 | (message as ɵParsedMessage).id ||
29 | (message as ParsedMessageLegacy).messageId
30 | ] =
31 | (message as ɵParsedMessage).text ||
32 | (message as ParsedMessageLegacy).messageString;
33 | });
34 | return JSON.stringify(fileObj, null, 2);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | 🌐 **Locl is an internationalization (i18n) tools suite for Angular.**
4 |
5 | ### Demos
6 | You can find a complete demo in the [apps/demo folder](apps/demo) and another one simpler [on StackBlitz](https://stackblitz.com/edit/ivy-ovy4cd) (it can take a long time to load the first time because ivy support on StackBlitz is still WIP).
7 |
8 | ### Core
9 | A library with various utility functions to help you with \$localize.
10 |
11 | - [Documentation](libs/core)
12 |
13 | ### CLI
14 | Dev tools to help you with `$localize` and Angular i18n.
15 |
16 | - [Documentation](libs/cli)
17 |
18 | ### Roadmap
19 | We have a bunch of features planned on our roadmap, but you can see the features requested by the community and vote for the ones that you want the most on https://locl.hellonext.co/.
20 |
21 | ### License
22 |
23 | Locl tools and libraries are distributed under the [AGPL v3.0 license]().
24 | You can use them for free in open source projects, but if you want to use them in commercial products with close source you are required to buy a proprietary license (coming soon).
25 |
--------------------------------------------------------------------------------
/apps/electron/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@locl/app",
3 | "version": "0.0.1",
4 | "description": "Translation app by Locl",
5 | "main": "index.js",
6 | "author": {
7 | "name": "Olivier Combe",
8 | "email": "olivier@locl.app"
9 | },
10 | "homepage": "https://www.locl.app/",
11 | "repository": {
12 | "url": "https://github.com/loclapp/locl"
13 | },
14 | "license": "MIT",
15 | "build": {
16 | "appId": "com.locl.app",
17 | "productName": "App",
18 | "copyright": "Copyright © 2018-2019 locl",
19 | "asar": false,
20 | "npmRebuild": false,
21 | "directories": {
22 | "buildResources": "icons",
23 | "output": "../electronapp-packages"
24 | },
25 | "mac": {
26 | "category": "public.app-category.developer-tools",
27 | "icon": "icon.png"
28 | },
29 | "win": {
30 | "target": "nsis",
31 | "icon": "icon.ico"
32 | },
33 | "linux": {
34 | "icon": "icon.png",
35 | "target": [
36 | "AppImage",
37 | "deb",
38 | "tar.gz"
39 | ],
40 | "synopsis": "App",
41 | "category": "Development"
42 | },
43 | "nsis": {
44 | "createDesktopShortcut": "always",
45 | "installerIcon": "icon.ico",
46 | "artifactName": "App-Setup-${version}.${ext}"
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tools/commitlint-plugin-body-content/commitlint-plugin-body-content.js:
--------------------------------------------------------------------------------
1 | const message = require('@commitlint/message');
2 |
3 | function getErrorMsg(negated, value, types) {
4 | return message.default([
5 | 'body',
6 | negated ? 'may not' : 'must',
7 | `contain [${value}] for scopes [${types.join(', ')}]`,
8 | ]);
9 | }
10 |
11 | module.exports = {
12 | rules: {
13 | 'body-content': function (parsed, when, params) {
14 | const body = parsed.body;
15 | const footer = parsed.footer;
16 | const type = parsed.type;
17 | const negated = when === 'never';
18 | const value = params[0];
19 | const types = params[1];
20 | if (!body && !footer) {
21 | if (negated || types.indexOf(type) === -1) {
22 | return [true];
23 | } else {
24 | return [false, getErrorMsg(negated, value, types)];
25 | }
26 | }
27 |
28 | const hasRegexp =
29 | (body
30 | ? body.split(/[\r\n]+/).find((str) => str.search(value) !== -1)
31 | : false) ||
32 | (footer
33 | ? footer.split(/[\r\n]+/).find((str) => str.search(value) !== -1)
34 | : false);
35 |
36 | return [
37 | negated ? !hasRegexp : hasRegexp,
38 | getErrorMsg(negated, value, types),
39 | ];
40 | },
41 | },
42 | };
43 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/extract/extractor.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 | import { ɵParsedMessage } from '@angular/localize';
9 | import { transformSync } from '@babel/core';
10 |
11 | import { makeEs2015ExtractPlugin } from './source_files/es2015_extract_plugin';
12 | import { makeEs5ExtractPlugin } from './source_files/es5_extract_plugin';
13 | import { Diagnostics } from '../common/diagnostics';
14 |
15 | /**
16 | * A class that extracts parsed messages from file contents, by parsing the contents as JavaScript
17 | * and looking for occurrences of `$localize` in the source code.
18 | */
19 | export class Extractor {
20 | messages: ɵParsedMessage[] = [];
21 |
22 | constructor(private diagnostics: Diagnostics) {}
23 |
24 | extractMessages(sourceCode: string): void {
25 | if (sourceCode.includes('$localize')) {
26 | // Only bother to parse the file if it contains a reference to `$localize`.
27 | transformSync(sourceCode, {
28 | plugins: [
29 | makeEs2015ExtractPlugin(this.messages, this.diagnostics),
30 | makeEs5ExtractPlugin(this.messages, this.diagnostics)
31 | ],
32 | code: false,
33 | ast: false
34 | });
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/convert.ts:
--------------------------------------------------------------------------------
1 | import { convertFiles, TranslationFormat } from './convert/convert';
2 | import { resolve } from 'path';
3 | import { Diagnostics } from './common/diagnostics';
4 |
5 | export const command = 'convert';
6 | export const describe = 'Convert translation files from one format to another';
7 | export const builder = {
8 | s: {
9 | alias: 'source',
10 | required: true,
11 | describe:
12 | 'A glob pattern indicating what files to convert, e.g. `./assets/**/*.xlf`. This can be absolute or relative to the current working directory. Only translation files are supported (json, xtb & xlf but not xmb).',
13 | },
14 | f: {
15 | alias: 'format',
16 | required: true,
17 | describe: 'The format of the translation files to generate.',
18 | choices: ['json', 'xlf', 'xtb', 'xlf2'],
19 | default: 'json',
20 | },
21 | o: {
22 | alias: 'outputPath',
23 | required: true,
24 | describe:
25 | 'A path to where the converted files will be written. This can be absolute or relative to the current working directory.',
26 | },
27 | };
28 |
29 | export const handler = function (options) {
30 | const diagnostics = new Diagnostics();
31 | convertFiles({
32 | sourceGlob: resolve(options['s']),
33 | format: options['f'] as TranslationFormat,
34 | outputPath: resolve(options['o']),
35 | diagnostics,
36 | });
37 | diagnostics.logMessages();
38 | process.exit(diagnostics.hasErrors ? 1 : 0);
39 | };
40 |
--------------------------------------------------------------------------------
/libs/app/src/web/src/core.module.ts:
--------------------------------------------------------------------------------
1 | import { APP_BASE_HREF } from '@angular/common';
2 | import { HttpClientModule } from '@angular/common/http';
3 | import { NgModule, Optional, SkipSelf } from '@angular/core';
4 | import { BrowserModule } from '@angular/platform-browser';
5 | import { throwIfAlreadyLoaded } from '@locl/utils';
6 | // libs
7 | import {
8 | CORE_PROVIDERS,
9 | PlatformLanguageToken,
10 | PlatformWindowToken,
11 | WindowPlatformService
12 | } from './services';
13 |
14 | // bring in custom web services here...
15 |
16 | // factories
17 | export function winFactory() {
18 | return window;
19 | }
20 |
21 | export function platformLangFactory() {
22 | const browserLang = window.navigator.language || 'en'; // fallback English
23 | // browser language has 2 codes, ex: 'en-US'
24 | return browserLang.split('-')[0];
25 | }
26 |
27 | export const BASE_PROVIDERS: any[] = [
28 | ...CORE_PROVIDERS,
29 | {
30 | provide: APP_BASE_HREF,
31 | useValue: '/'
32 | }
33 | ];
34 |
35 | @NgModule({
36 | imports: [
37 | BrowserModule,
38 | HttpClientModule
39 | ],
40 | providers: [
41 | ...BASE_PROVIDERS, {
42 | provide: PlatformLanguageToken,
43 | useFactory: platformLangFactory
44 | }, {
45 | provide: PlatformWindowToken,
46 | useFactory: winFactory
47 | }, {
48 | provide: WindowPlatformService,
49 | useFactory: winFactory
50 | }
51 | ]
52 | })
53 | export class CoreModule {
54 | constructor(
55 | @Optional()
56 | @SkipSelf()
57 | parentModule: CoreModule
58 | ) {
59 | throwIfAlreadyLoaded(parentModule, 'CoreModule');
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/libs/app/src/web/src/services/log.service.ts:
--------------------------------------------------------------------------------
1 | // angular
2 | import { Injectable } from '@angular/core';
3 |
4 | export interface IDebug {
5 | LEVEL_1: boolean;
6 | LEVEL_2: boolean;
7 | LEVEL_3: boolean;
8 | LEVEL_4: boolean;
9 | LEVEL_5: boolean;
10 |
11 | [key: string]: boolean;
12 | }
13 |
14 | @Injectable()
15 | export class LogService {
16 | public static DEBUG: IDebug = {
17 | LEVEL_1: false, // .warn only
18 | LEVEL_2: false, // .error only
19 | LEVEL_3: false, // .log + all the above
20 | LEVEL_4: false, // .log + all the above + info
21 | LEVEL_5: false // just info (excluding all else)
22 | };
23 |
24 | // info (extra messages like analytics)
25 | // use LEVEL_5 to see only these
26 | public info(...msg: Array) {
27 | if (LogService.DEBUG.LEVEL_5 || LogService.DEBUG.LEVEL_4) {
28 | // extra messages
29 | console.info(msg);
30 | }
31 | }
32 |
33 | // debug (standard output)
34 | public debug(...msg: Array) {
35 | if (LogService.DEBUG.LEVEL_4 || LogService.DEBUG.LEVEL_3) {
36 | // console.debug does not work on {N} apps... use `log`
37 | console.log(msg);
38 | }
39 | }
40 |
41 | // error
42 | public error(...err: Array) {
43 | if (
44 | LogService.DEBUG.LEVEL_4 ||
45 | LogService.DEBUG.LEVEL_3 ||
46 | LogService.DEBUG.LEVEL_2
47 | ) {
48 | console.error(err);
49 | }
50 | }
51 |
52 | // warn
53 | public warn(...warn: Array) {
54 | if (
55 | LogService.DEBUG.LEVEL_4 ||
56 | LogService.DEBUG.LEVEL_3 ||
57 | LogService.DEBUG.LEVEL_1
58 | ) {
59 | console.warn(warn);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/extract.ts:
--------------------------------------------------------------------------------
1 | import { Diagnostics } from './common/diagnostics';
2 | import { TranslationFormat } from './common/util';
3 | import { extractTranslations } from './extract/extract';
4 |
5 | export const command = 'extract';
6 | export const describe = 'Extract translations from your ivy application';
7 | export const builder = {
8 | s: {
9 | alias: 'source',
10 | required: true,
11 | describe:
12 | 'A glob pattern indicating what files to search for translations, e.g. `./dist/**/*.js`. This can be absolute or relative to the current working directory.',
13 | },
14 | f: {
15 | alias: 'format',
16 | required: true,
17 | describe: 'The format of the translation files to generate.',
18 | choices: ['json', 'xlf', 'xmb', 'xlf2'],
19 | default: 'json',
20 | },
21 | o: {
22 | alias: 'outputPath',
23 | required: true,
24 | describe:
25 | 'A path to where the translation file will be written. This can be absolute or relative to the current working directory.',
26 | },
27 | l: {
28 | alias: ['locale', 'locales'],
29 | required: false,
30 | type: 'array',
31 | describe:
32 | 'The locale for the extracted file, "en" by default. If you use multiple locales (e.g. "en fr es"), a new file will be generated for each locale',
33 | },
34 | };
35 |
36 | export const handler = function (options) {
37 | const diagnostics = new Diagnostics();
38 | extractTranslations({
39 | sourceGlob: options['s'] as string,
40 | format: options['f'] as TranslationFormat,
41 | outputPath: options['o'] as string,
42 | locales: options['l'] as string[],
43 | diagnostics,
44 | });
45 | diagnostics.logMessages();
46 | process.exit(diagnostics.hasErrors ? 1 : 0);
47 | };
48 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/extract/source_files/es2015_extract_plugin.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 | import { ɵParsedMessage, ɵparseMessage } from '@angular/localize';
9 | import { NodePath, PluginObj } from '@babel/core';
10 | import { TaggedTemplateExpression } from '@babel/types';
11 | import { ParsedMessageLegacy } from '../../../cmds/common/util';
12 |
13 | import {
14 | isGlobalIdentifier,
15 | isNamedIdentifier,
16 | unwrapMessagePartsFromTemplateLiteral
17 | } from '../source_file_utils';
18 | import { Diagnostics } from '../../common/diagnostics';
19 |
20 | export function makeEs2015ExtractPlugin(
21 | messages: (ɵParsedMessage | ParsedMessageLegacy)[],
22 | diagnostics: Diagnostics,
23 | localizeName = '$localize'
24 | ): PluginObj {
25 | return {
26 | visitor: {
27 | TaggedTemplateExpression(path: NodePath) {
28 | const tag = path.get('tag');
29 | if (isNamedIdentifier(tag, localizeName) && isGlobalIdentifier(tag)) {
30 | const messageParts = unwrapMessagePartsFromTemplateLiteral(
31 | path.node.quasi.quasis
32 | );
33 | const message: ɵParsedMessage | ParsedMessageLegacy = ɵparseMessage(
34 | messageParts,
35 | path.node.quasi.expressions
36 | );
37 | if (
38 | !messages.find((msg: any) =>
39 | message.id
40 | ? msg.id === message.id
41 | : msg.messageId ===
42 | ((message) as ParsedMessageLegacy).messageId
43 | )
44 | ) {
45 | messages.push(message);
46 | }
47 | }
48 | }
49 | }
50 | };
51 | }
52 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/extract/source_files/es5_extract_plugin.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 | import { ɵParsedMessage, ɵparseMessage } from '@angular/localize';
9 | import { NodePath, PluginObj } from '@babel/core';
10 | import { CallExpression } from '@babel/types';
11 | import { ParsedMessageLegacy } from '../../../cmds/common/util';
12 |
13 | import {
14 | isGlobalIdentifier,
15 | isNamedIdentifier,
16 | unwrapMessagePartsFromLocalizeCall,
17 | unwrapSubstitutionsFromLocalizeCall
18 | } from '../source_file_utils';
19 | import { Diagnostics } from '../../common/diagnostics';
20 |
21 | export function makeEs5ExtractPlugin(
22 | messages: (ɵParsedMessage | ParsedMessageLegacy)[],
23 | diagnostics: Diagnostics,
24 | localizeName = '$localize'
25 | ): PluginObj {
26 | return {
27 | visitor: {
28 | CallExpression(callPath: NodePath) {
29 | const calleePath = callPath.get('callee');
30 | if (
31 | isNamedIdentifier(calleePath, localizeName) &&
32 | isGlobalIdentifier(calleePath)
33 | ) {
34 | const messageParts = unwrapMessagePartsFromLocalizeCall(callPath);
35 | const expressions = unwrapSubstitutionsFromLocalizeCall(
36 | callPath.node
37 | );
38 | const message: ɵParsedMessage | ParsedMessageLegacy = ɵparseMessage(
39 | messageParts,
40 | expressions
41 | );
42 | if (
43 | !messages.find((msg: any) =>
44 | message.id
45 | ? msg.id === message.id
46 | : msg.messageId ===
47 | ((message) as ParsedMessageLegacy).messageId
48 | )
49 | ) {
50 | messages.push(message);
51 | }
52 | }
53 | }
54 | }
55 | };
56 | }
57 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/common/diagnostics.ts:
--------------------------------------------------------------------------------
1 | import * as chalk from 'chalk';
2 |
3 | export declare type DiagnosticHandlingStrategy = 'error' | 'warning' | 'ignore';
4 |
5 | /**
6 | * This class is used to collect and then report warnings and errors that occur during the execution
7 | * of the tools.
8 | */
9 | export class Diagnostics {
10 | readonly messages = [];
11 |
12 | get hasErrors() {
13 | return this.messages.some((m) => m.type === 'error');
14 | }
15 |
16 | add(type: DiagnosticHandlingStrategy, message: string) {
17 | if (type !== 'ignore') {
18 | this.messages.push({ type, message });
19 | }
20 | }
21 |
22 | merge(other: Diagnostics) {
23 | this.messages.push(...other.messages);
24 | }
25 |
26 | log(message: string) {
27 | this.messages.push({ type: '', message });
28 | }
29 |
30 | warn(message: string) {
31 | this.messages.push({ type: 'warning', message });
32 | }
33 |
34 | error(message: string) {
35 | this.messages.push({ type: 'error', message });
36 | }
37 |
38 | formatDiagnostics(message: string): string {
39 | const errors = this.messages!.filter((d) => d.type === 'error').map(
40 | (d) => ' - ' + d.message
41 | );
42 | const warnings = this.messages!.filter((d) => d.type === 'warning').map(
43 | (d) => ' - ' + d.message
44 | );
45 | if (errors.length) {
46 | message += '\nERRORS:\n' + errors.join('\n');
47 | }
48 | if (warnings.length) {
49 | message += '\nWARNINGS:\n' + warnings.join('\n');
50 | }
51 | return message;
52 | }
53 |
54 | logMessages() {
55 | while (this.messages.length) {
56 | const m = this.messages.shift();
57 | switch (m.type) {
58 | case 'warning':
59 | console.warn(chalk.yellow(`Warning: ${m.message}`));
60 | break;
61 | case 'error':
62 | console.error(chalk.red(`Error: ${m.message}`));
63 | break;
64 | default:
65 | console.log(chalk.blue(`${m.message}`));
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/apps/web/src/styles.scss:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 | @import '~@locl/scss';
3 | @import "~bulma/sass/utilities/_all";
4 | @import "~bulma/sass/base/_all";
5 |
6 | /*** ELEMENTS ***/
7 | //@import "~bulma/sass/elements/box.sass";
8 | @import "~bulma/sass/elements/button.sass";
9 | //@import "~bulma/sass/elements/container.sass";
10 | //@import "~bulma/sass/elements/content.sass";
11 | //@import "~bulma/sass/elements/icon.sass";
12 | //@import "~bulma/sass/elements/image.sass";
13 | //@import "~bulma/sass/elements/notification.sass";
14 | //@import "~bulma/sass/elements/progress.sass";
15 | //@import "~bulma/sass/elements/table.sass";
16 | //@import "~bulma/sass/elements/tag.sass";
17 | //@import "~bulma/sass/elements/title.sass";
18 | //@import "~bulma/sass/elements/other.sass";
19 |
20 | /*** FORMS ***/
21 | @import "~bulma/sass/form/shared.sass";
22 | @import "~bulma/sass/form/input-textarea.sass";
23 | //@import "~bulma/sass/form/checkbox-radio.sass";
24 | //@import "~bulma/sass/form/select.sass";
25 | //@import "~bulma/sass/form/file.sass";
26 | //@import "~bulma/sass/form/tools.sass";
27 |
28 | /*** COMPONENTS ***/
29 | //@import "~bulma/sass/components/breadcrumb.sass";
30 | //@import "~bulma/sass/components/card.sass";
31 | //@import "~bulma/sass/components/dropdown.sass";
32 | //@import "~bulma/sass/components/level.sass";
33 | //@import "~bulma/sass/components/list.sass";
34 | //@import "~bulma/sass/components/media.sass";
35 | //@import "~bulma/sass/components/menu.sass";
36 | //@import "~bulma/sass/components/message.sass";
37 | //@import "~bulma/sass/components/modal.sass";
38 | //@import "~bulma/sass/components/navbar.sass";
39 | //@import "~bulma/sass/components/pagination.sass";
40 | //@import "~bulma/sass/components/panel.sass";
41 | //@import "~bulma/sass/components/tabs.sass";
42 |
43 | /*** GRID ***/
44 | @import "~bulma/sass/grid/columns.sass";
45 | @import "~bulma/sass/grid/tiles.sass";
46 |
47 | /*** LAYOUT ***/
48 | //@import "~bulma/sass/layout/hero.sass";
49 | //@import "~bulma/sass/layout/section.sass";
50 | //@import "~bulma/sass/layout/footer.sass";
--------------------------------------------------------------------------------
/libs/cli/src/cmds/common/file_utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 | import * as fs from 'fs';
9 | import * as path from 'path';
10 |
11 | export class FileUtils {
12 | static readFile(absolutePath: string): string {
13 | return fs.readFileSync(absolutePath, 'utf8');
14 | }
15 |
16 | static readFileBuffer(absolutePath: string): Buffer {
17 | return fs.readFileSync(absolutePath);
18 | }
19 |
20 | static writeFile(absolutePath: string, contents: string | Buffer) {
21 | FileUtils.ensureDir(path.dirname(absolutePath));
22 | fs.writeFileSync(absolutePath, contents);
23 | }
24 |
25 | static ensureDir(absolutePath: string): void {
26 | const parents: string[] = [];
27 | while (!FileUtils.isRoot(absolutePath) && !fs.existsSync(absolutePath)) {
28 | parents.push(absolutePath);
29 | absolutePath = path.dirname(absolutePath);
30 | }
31 | while (parents.length) {
32 | fs.mkdirSync(parents.pop()!);
33 | }
34 | }
35 |
36 | static remove(p: string): void {
37 | const stat = fs.statSync(p);
38 | if (stat.isFile()) {
39 | fs.unlinkSync(p);
40 | } else if (stat.isDirectory()) {
41 | fs.readdirSync(p).forEach(child => {
42 | const absChild = path.resolve(p, child);
43 | FileUtils.remove(absChild);
44 | });
45 | fs.rmdirSync(p);
46 | }
47 | }
48 |
49 | static isRoot(absolutePath: string): boolean {
50 | return path.dirname(absolutePath) === absolutePath;
51 | }
52 |
53 | static dedup(files: string[], pattern: RegExp, replaceValue = ''): string[] {
54 | const filesSet = files.map(file => file.replace(pattern, replaceValue));
55 | const dedup = [];
56 | const indexes = [];
57 | filesSet.forEach((file: string, index: number) => {
58 | if (dedup.indexOf(file) === -1) {
59 | dedup.push(file);
60 | indexes.push(index);
61 | }
62 | });
63 | return indexes.map(index => files[index]);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/apps/web/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Locl - I18n tools suite for Angular
9 |
10 |
12 |
13 |
14 |
15 |
16 |
17 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/extract/translation_files/xmb_translation_serializer.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 | import { ɵParsedMessage } from '@angular/localize';
9 | import { ParsedMessageLegacy } from '../../../cmds/common/util';
10 | import { TranslationSerializer } from './translation_serializer';
11 | import { XmlFile } from './xml_file';
12 |
13 | const DOCTYPE = `
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | `;
32 |
33 | export class XmbTranslationSerializer implements TranslationSerializer {
34 | renderFile(messages: (ɵParsedMessage | ParsedMessageLegacy)[]): string {
35 | const xml = new XmlFile();
36 | xml.startTag('messagebundle');
37 | messages.forEach(message => {
38 | xml.startTag(
39 | 'msg',
40 | {
41 | id:
42 | (message as ɵParsedMessage).id ||
43 | (message as ParsedMessageLegacy).messageId,
44 | desc: message.description,
45 | meaning: message.meaning
46 | },
47 | { preserveWhitespace: true }
48 | );
49 | this.renderMessage(xml, message);
50 | xml.endTag('msg', { preserveWhitespace: false });
51 | });
52 | xml.endTag('messagebundle');
53 | return xml.toString();
54 | }
55 |
56 | private renderMessage(
57 | xml: XmlFile,
58 | message: ɵParsedMessage | ParsedMessageLegacy
59 | ): void {
60 | xml.text(message.messageParts[0]);
61 | for (let i = 1; i < message.messageParts.length; i++) {
62 | xml.startTag(
63 | 'ph',
64 | { name: message.placeholderNames[i - 1] },
65 | { selfClosing: true }
66 | );
67 | xml.text(message.messageParts[i]);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/libs/cli/test/cmds/convert.spec.ts:
--------------------------------------------------------------------------------
1 | import { convertFiles } from '../../src/cmds/convert/convert';
2 | import { Diagnostics } from '../../src/cmds/common/diagnostics';
3 | import {
4 | mockFile,
5 | simpleTranslatedXlf,
6 | translatedJSON,
7 | translatedXlf,
8 | translatedXLF2,
9 | translatedXtb,
10 | } from './mock';
11 | import { FileUtils } from '../../src/cmds/common/file_utils';
12 | import { runInEachFileSystem } from '@angular/compiler-cli/src/ngtsc/file_system/testing';
13 |
14 | runInEachFileSystem(() => {
15 | describe('convertFiles', () => {
16 | it('should convert xlf to json', function () {
17 | mockFile(['fr.xlf', '/i18n/fr.json'], [translatedXlf]);
18 | const spy = spyOn(FileUtils, 'writeFile');
19 |
20 | convertFiles({
21 | format: 'json',
22 | sourceGlob: 'fr.xlf',
23 | outputPath: '/i18n/fr.json',
24 | diagnostics: new Diagnostics(),
25 | });
26 |
27 | expect(spy).toHaveBeenCalledWith('/i18n/fr.json', translatedJSON);
28 | });
29 |
30 | it('should convert json to xlf', function () {
31 | mockFile(['fr.json', '/i18n/fr.xlf'], [translatedJSON]);
32 | const spy = spyOn(FileUtils, 'writeFile');
33 |
34 | convertFiles({
35 | format: 'xlf',
36 | sourceGlob: 'fr.json',
37 | outputPath: '/i18n/fr.xlf',
38 | diagnostics: new Diagnostics(),
39 | });
40 |
41 | expect(spy).toHaveBeenCalledWith('/i18n/fr.xlf', simpleTranslatedXlf);
42 | });
43 |
44 | it('should convert xlf to xtb', function () {
45 | mockFile(['fr.xlf', '/i18n/fr.xtb'], [translatedXlf]);
46 | const spy = spyOn(FileUtils, 'writeFile');
47 |
48 | convertFiles({
49 | format: 'xtb',
50 | sourceGlob: 'fr.xlf',
51 | outputPath: '/i18n/fr.xtb',
52 | diagnostics: new Diagnostics(),
53 | });
54 |
55 | expect(spy).toHaveBeenCalledWith('/i18n/fr.xtb', translatedXtb);
56 | });
57 |
58 | it('should convert xtb to xlf2', function () {
59 | mockFile(['fr.xtb', '/i18n/fr.xlf'], [translatedXtb]);
60 | const spy = spyOn(FileUtils, 'writeFile');
61 |
62 | convertFiles({
63 | format: 'xlf2',
64 | sourceGlob: 'fr.xtb',
65 | outputPath: '/i18n/fr.xlf',
66 | diagnostics: new Diagnostics(),
67 | });
68 |
69 | expect(spy).toHaveBeenCalledWith('/i18n/fr.xlf', translatedXLF2);
70 | });
71 | });
72 | });
73 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/extract/translation_files/xtb_translation_serializer.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 | import { ɵParsedMessage } from '@angular/localize';
9 | import { ParsedMessageLegacy } from '../../../cmds/common/util';
10 | import { TranslationSerializer } from './translation_serializer';
11 | import { XmlFile } from './xml_file';
12 |
13 | const DOCTYPE = `
14 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | ]>
29 | `;
30 |
31 | export class XtbTranslationSerializer implements TranslationSerializer {
32 | renderFile(
33 | messages: (ɵParsedMessage | ParsedMessageLegacy)[],
34 | locale: string
35 | ): string {
36 | const xml = new XmlFile();
37 | xml.startTag('translationbundle', { lang: locale });
38 | messages.forEach(message => {
39 | xml.startTag(
40 | 'translation',
41 | {
42 | id:
43 | (message as ɵParsedMessage).id ||
44 | (message as ParsedMessageLegacy).messageId,
45 | desc: message.description,
46 | meaning: message.meaning
47 | },
48 | { preserveWhitespace: true }
49 | );
50 | this.renderMessage(xml, message);
51 | xml.endTag('translation', { preserveWhitespace: false });
52 | });
53 | xml.endTag('translationbundle');
54 | return DOCTYPE + xml.toString();
55 | }
56 |
57 | private renderMessage(
58 | xml: XmlFile,
59 | message: ɵParsedMessage | ParsedMessageLegacy
60 | ): void {
61 | xml.text(message.messageParts[0]);
62 | for (let i = 1; i < message.messageParts.length; i++) {
63 | xml.startTag(
64 | 'ph',
65 | { name: message.placeholderNames[i - 1] },
66 | { selfClosing: true }
67 | );
68 | xml.text(message.messageParts[i]);
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/libs/core/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Locl Core
4 |
5 | A library with various utility functions to help you with `$localize` (if you need help to get started with `$localize`, [read this](https://blog.ninja-squad.com/2019/12/10/angular-localize/)).
6 |
7 | [Demo on StackBlitz](https://stackblitz.com/edit/ivy-ovy4cd) (it can take a long time to load the first time because ivy support on StackBlitz is still WIP).
8 |
9 | ## Installation
10 |
11 | Install the core library with npm:
12 |
13 | ```sh
14 | npm install @locl/core --save
15 | ```
16 |
17 | ## Usage
18 |
19 | ### Loading the translations at runtime
20 |
21 | Angular translates the templates as soon as the components are loaded, which means that if you want to load the translations at runtime, you need to load them before the application starts.
22 | The best way to do that, is to load the translations before `bootstrapModule` gets called.
23 |
24 | `@locl/core` provides two functions to help you get the files over http:
25 |
26 | - `getTranslations(url: string, method?: 'GET'|'POST', headers?: {[key: string]: string}, async?: boolean): Promise`: Gets a translation file from a server using an XHR HTTP request
27 | - `fetchTranslations(url: string, method?: 'GET'|'POST', headers?: {[key: string]: string}): Promise`: Gets a translation file from a server using the fetch API (see [browser compatibility for the fetch API on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API))
28 |
29 | Both methods return a promise with a `ParsedTranslationBundle` object containing the translations and the locale. Common usage is the following:
30 |
31 | ```ts
32 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
33 | import { getTranslations, ParsedTranslationBundle } from '@locl/core';
34 | import { AppModule } from './app/app.module';
35 |
36 | getTranslations('/assets/i18n/fr.json').then(
37 | (data: ParsedTranslationBundle) => {
38 | platformBrowserDynamic()
39 | .bootstrapModule(AppModule)
40 | .catch(err => console.error(err));
41 | }
42 | );
43 | ```
44 |
45 | - `loadTranslations(parsedTranslationBundle: ParsedTranslationBundle)`: Finishes initialization of \$localize, loads translations in memory and sets the `LOCALE_ID` value.
46 | Use this **only** if you're not using any of the two functions `getTranslations` or `fetchTranslations`.
47 |
48 | ### Util functions
49 |
50 | - `getBrowserLang(): string`: Returns the current browser lang (e.g. "fr") if available, or an empty string otherwise
51 | - `getBrowserCultureLang(): string`: Returns the current browser culture language name (e.g. "fr-FR") if available, or an empty string otherwise
52 |
--------------------------------------------------------------------------------
/apps/demo/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | /*
2 | * Remove template code below
3 | */
4 | :host {
5 | display: block;
6 | font-family: sans-serif;
7 | min-width: 300px;
8 | max-width: 600px;
9 | margin: 50px auto;
10 | }
11 |
12 | .gutter-left {
13 | margin-left: 9px;
14 | }
15 |
16 | .col-span-2 {
17 | grid-column: span 2;
18 | }
19 |
20 | .flex {
21 | display: flex;
22 | align-items: center;
23 | justify-content: center;
24 | }
25 |
26 | header {
27 | background-color: #143055;
28 | color: white;
29 | padding: 5px;
30 | border-radius: 3px;
31 | }
32 |
33 | main {
34 | padding: 0 36px;
35 | }
36 |
37 | p {
38 | text-align: center;
39 | }
40 |
41 | h1 {
42 | text-align: center;
43 | margin-left: 18px;
44 | font-size: 24px;
45 | }
46 |
47 | h2 {
48 | text-align: center;
49 | font-size: 20px;
50 | margin: 40px 0 10px 0;
51 | }
52 |
53 | .resources {
54 | text-align: center;
55 | list-style: none;
56 | padding: 0;
57 | display: grid;
58 | grid-gap: 9px;
59 | grid-template-columns: 1fr 1fr;
60 | }
61 |
62 | .resource {
63 | color: #0094ba;
64 | height: 36px;
65 | background-color: rgba(0, 0, 0, 0);
66 | border: 1px solid rgba(0, 0, 0, 0.12);
67 | border-radius: 4px;
68 | padding: 3px 9px;
69 | text-decoration: none;
70 | }
71 |
72 | .resource:hover {
73 | background-color: rgba(68, 138, 255, 0.04);
74 | }
75 |
76 | pre {
77 | padding: 9px;
78 | border-radius: 4px;
79 | background-color: black;
80 | color: #eee;
81 | }
82 |
83 | details {
84 | border-radius: 4px;
85 | color: #333;
86 | background-color: rgba(0, 0, 0, 0);
87 | border: 1px solid rgba(0, 0, 0, 0.12);
88 | padding: 3px 9px;
89 | margin-bottom: 9px;
90 | }
91 |
92 | summary {
93 | cursor: pointer;
94 | outline: none;
95 | height: 36px;
96 | line-height: 36px;
97 | }
98 |
99 | .github-star-container {
100 | margin-top: 12px;
101 | line-height: 20px;
102 | }
103 |
104 | .github-star-container a {
105 | display: flex;
106 | align-items: center;
107 | text-decoration: none;
108 | color: #333;
109 | }
110 |
111 | .github-star-badge {
112 | color: #24292e;
113 | display: flex;
114 | align-items: center;
115 | font-size: 12px;
116 | padding: 3px 10px;
117 | border: 1px solid rgba(27, 31, 35, 0.2);
118 | border-radius: 3px;
119 | background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
120 | margin-left: 4px;
121 | font-weight: 600;
122 | }
123 |
124 | .github-star-badge:hover {
125 | background-image: linear-gradient(-180deg, #f0f3f6, #e6ebf1 90%);
126 | border-color: rgba(27, 31, 35, 0.35);
127 | background-position: -0.5em;
128 | }
129 | .github-star-badge .material-icons {
130 | height: 16px;
131 | width: 16px;
132 | margin-right: 4px;
133 | }
134 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/convert/translation_parsers/translation_parser.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 | import { Diagnostics } from '../../common/diagnostics';
9 | import { ParsedTranslation } from '../translations';
10 | import { ɵMessageId } from '@angular/localize';
11 |
12 | /**
13 | * An object that holds translations that have been parsed from a translation file.
14 | */
15 | export interface ParsedTranslationBundle {
16 | locale: string | undefined;
17 | sources?: Record<ɵMessageId, ParsedTranslation>;
18 | translations: Record<ɵMessageId, ParsedTranslation>;
19 | diagnostics: Diagnostics;
20 | }
21 |
22 | /**
23 | * Indicates that a parser can parse a given file, with a hint that can be used to speed up actual
24 | * parsing.
25 | */
26 | export interface CanParseAnalysis {
27 | canParse: true;
28 | diagnostics: Diagnostics;
29 | hint: Hint;
30 | }
31 |
32 | /**
33 | * Indicates that a parser cannot parse a given file with diagnostics as why this is.
34 | * */
35 | export interface CannotParseAnalysis {
36 | canParse: false;
37 | diagnostics: Diagnostics;
38 | }
39 |
40 | /**
41 | * Information about whether a `TranslationParser` can parse a given file.
42 | */
43 | export type ParseAnalysis = CanParseAnalysis | CannotParseAnalysis;
44 |
45 | /**
46 | * Implement this interface to provide a class that can parse the contents of a translation file.
47 | */
48 | export interface TranslationParser {
49 | /**
50 | * Parses the given file, extracting the target locale and translations.
51 | *
52 | * @param filePath The absolute path to the translation file.
53 | * @param contents The contents of the translation file.
54 | */
55 | parse(filePath: string, contents: string): ParsedTranslationBundle;
56 |
57 | /**
58 | * Returns true if this parser can parse the given file.
59 | *
60 | * @param filePath The absolute path to the translation file.
61 | * @param contents The contents of the translation file.
62 | * @param hint A value that can be used by the parser to speed up parsing of the file. This will
63 | * have been provided as the return result from calling `canParse()`.
64 | */
65 | canParse(filePath: string, contents: string, hint: Hint): Hint | false;
66 |
67 | /**
68 | * Analyze the file to see if this parser can parse the given file.
69 | *
70 | * @param filePath The absolute path to the translation file.
71 | * @param contents The contents of the translation file.
72 | * @returns Information indicating whether the file can be parsed by this parser.
73 | */
74 | analyze(filePath: string, contents: string): ParseAnalysis;
75 | }
76 |
--------------------------------------------------------------------------------
/apps/web/src/assets/img/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libs/cli/test/cmds/extract.spec.ts:
--------------------------------------------------------------------------------
1 | import { runInEachFileSystem } from '@angular/compiler-cli/src/ngtsc/file_system/testing';
2 | import { Diagnostics } from '../../src/cmds/common/diagnostics';
3 | import { FileUtils } from '../../src/cmds/common/file_utils';
4 | import { extractTranslations } from '../../src/cmds/extract/extract';
5 | import { mockFile, targetXlf } from './mock';
6 |
7 | runInEachFileSystem(() => {
8 | describe('extract', () => {
9 | it('should work', () => {
10 | mockFile(['main-es2015.js', '/i18/en.xlf'], [mainEs2015]);
11 | const spy = spyOn(FileUtils, 'writeFile');
12 |
13 | extractTranslations({
14 | format: 'xlf',
15 | sourceGlob: 'main-es2015.js',
16 | outputPath: '/i18/en.xlf',
17 | diagnostics: new Diagnostics(),
18 | });
19 |
20 | expect(spy).toHaveBeenCalledWith('/i18/en.xlf', targetXlf('en', false));
21 | });
22 |
23 | it('should create files automatically if output is a folder', () => {
24 | mockFile(['main-es2015.js', '/i18'], [mainEs2015]);
25 | const spy = spyOn(FileUtils, 'writeFile');
26 |
27 | extractTranslations({
28 | format: 'xlf',
29 | sourceGlob: 'main-es2015.js',
30 | outputPath: '/i18',
31 | diagnostics: new Diagnostics(),
32 | });
33 |
34 | expect(spy).toHaveBeenCalledWith(
35 | '/i18/main.en.xlf',
36 | targetXlf('en', false)
37 | );
38 | });
39 |
40 | it('should create multiple files if output is a folder and locales is an array', () => {
41 | mockFile(['main-es2015.js', '/i18'], [mainEs2015]);
42 | const spy = spyOn(FileUtils, 'writeFile');
43 |
44 | extractTranslations({
45 | format: 'xlf',
46 | sourceGlob: 'main-es2015.js',
47 | outputPath: '/i18',
48 | locales: ['en', 'fr'],
49 | diagnostics: new Diagnostics(),
50 | });
51 |
52 | expect(spy).toHaveBeenCalledTimes(2);
53 | expect(spy).toHaveBeenNthCalledWith(
54 | 1,
55 | '/i18/main.en.xlf',
56 | targetXlf('en', false)
57 | );
58 | expect(spy).toHaveBeenNthCalledWith(
59 | 2,
60 | '/i18/main.fr.xlf',
61 | targetXlf('fr', false)
62 | );
63 | });
64 | });
65 | });
66 |
67 | const mainEs2015 = `
68 | var I18N_0;
69 | if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) {
70 | const MSG_EXTERNAL_5105652583545451042$$APPS_DEMO_SRC_APP_APP_COMPONENT_TS_1 = goog.getMsg("It works!");
71 | I18N_0 = MSG_EXTERNAL_5105652583545451042$$APPS_DEMO_SRC_APP_APP_COMPONENT_TS_1;
72 | }
73 | else {
74 | I18N_0 = $localize \`:site header|An introduction header for this sample␟7e1a20ccc16692f73c6e224b489dc7e275ecc6ed␟3987846127133982403:It works!\`;
75 | }
76 | const name = '$localize';
77 | const lib = 'Locl';
78 | class AppComponent {
79 | constructor() {
80 | this.title = $localize \`Welcome to the demo of \${name} and \${lib} made for \${name}!\`;
81 | console.log($localize \`:@@foo:custom id!\`);
82 | }
83 | }`;
84 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/extract/translation_files/xliff1_translation_serializer.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 | import { ɵParsedMessage } from '@angular/localize';
9 | import { ParsedMessageLegacy } from '../../../cmds/common/util';
10 | import { TranslationSerializer } from './translation_serializer';
11 | import { XmlFile } from './xml_file';
12 |
13 | export class Xliff1TranslationSerializer implements TranslationSerializer {
14 | renderFile(
15 | messages: (ɵParsedMessage | ParsedMessageLegacy)[],
16 | locale: string,
17 | isTarget = false
18 | ): string {
19 | const xml = new XmlFile();
20 | xml.startTag('xliff', {
21 | version: '1.2',
22 | xmlns: 'urn:oasis:names:tc:xliff:document:1.2'
23 | });
24 | const fileAttrs = {
25 | ['source-language']: isTarget ? undefined : locale,
26 | ['target-language']: locale,
27 | datatype: 'plaintext'
28 | };
29 | xml.startTag('file', fileAttrs);
30 | xml.startTag('body');
31 | messages.forEach(message => {
32 | xml.startTag('trans-unit', {
33 | id:
34 | (message as ɵParsedMessage).id ||
35 | (message as ParsedMessageLegacy).messageId,
36 | datatype: 'html'
37 | });
38 | if (!isTarget) {
39 | this.generateMessageTag(xml, 'source', message);
40 | }
41 | this.generateMessageTag(xml, 'target', message);
42 | if (message.description) {
43 | this.renderNote(xml, 'description', message.description);
44 | }
45 | if (message.meaning) {
46 | this.renderNote(xml, 'meaning', message.meaning);
47 | }
48 | xml.endTag('trans-unit');
49 | });
50 | xml.endTag('body');
51 | xml.endTag('file');
52 | xml.endTag('xliff');
53 | return xml.toString();
54 | }
55 |
56 | private generateMessageTag(
57 | xml: XmlFile,
58 | tagName: string,
59 | message: ɵParsedMessage | ParsedMessageLegacy
60 | ) {
61 | xml.startTag(tagName, {}, { preserveWhitespace: true });
62 | this.renderMessage(xml, message);
63 | xml.endTag(tagName, { preserveWhitespace: false });
64 | }
65 |
66 | private renderMessage(
67 | xml: XmlFile,
68 | message: ɵParsedMessage | ParsedMessageLegacy
69 | ): void {
70 | xml.text(message.messageParts[0]);
71 | for (let i = 1; i < message.messageParts.length; i++) {
72 | xml.startTag(
73 | 'x',
74 | { id: message.placeholderNames[i - 1] },
75 | { selfClosing: true }
76 | );
77 | xml.text(message.messageParts[i]);
78 | }
79 | }
80 |
81 | private renderNote(xml: XmlFile, name: string, value: string) {
82 | xml.startTag(
83 | 'note',
84 | { priority: '1', from: name },
85 | { preserveWhitespace: true }
86 | );
87 | xml.text(value);
88 | xml.endTag('note', { preserveWhitespace: false });
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/libs/core/src/lib/init.ts:
--------------------------------------------------------------------------------
1 | // Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
2 | import '@angular/localize/init';
3 | import { ɵɵi18nPostprocess } from '@angular/core';
4 | import { loadTranslations as _loadTranslations } from '@angular/localize';
5 | import { ParsedTranslationBundle } from './interfaces';
6 | import { _global } from './global';
7 |
8 | /**
9 | * The $locl function is an emulation of $localize but it returns a `LoclString`
10 | * instead of a real string. A `LoclString` is only evaluated when the template
11 | * is created, not just when the file containing that template is loaded by the
12 | * browser (at bootstrap). This means that we can delay initialization of the
13 | * string values and wait for the translations to be loaded.
14 | *
15 | * @param messageParts
16 | * @param expressions
17 | */
18 | export const $locl = function (
19 | messageParts: TemplateStringsArray,
20 | ...expressions: readonly any[]
21 | ) {
22 | return new LoclString(messageParts, ...expressions);
23 | };
24 |
25 | // keep a local backup the of the real $localize
26 | const backUpLocalize = _global.$localize;
27 | // override $localize until everything is loaded
28 | _global.$localize = $locl;
29 | let isLoaded = false;
30 |
31 | class LoclString extends String {
32 | private readonly initParams: [TemplateStringsArray, ...(readonly any[])];
33 | private value: string = null;
34 | private postProcess = false;
35 |
36 | constructor(
37 | messageParts: TemplateStringsArray,
38 | ...expressions: readonly any[]
39 | ) {
40 | super();
41 | this.initParams = [messageParts, ...expressions];
42 | }
43 |
44 | toString(): string {
45 | if (this.value) {
46 | return this.value;
47 | }
48 | if (!isLoaded) {
49 | return backUpLocalize(...this.initParams);
50 | }
51 | this.value = _global.$localize(...this.initParams);
52 | if (this.postProcess) {
53 | this.value = ɵɵi18nPostprocess(this.value);
54 | this.postProcess = false;
55 | }
56 | return this.value;
57 | }
58 |
59 | valueOf(): string {
60 | return this.toString();
61 | }
62 |
63 | replace(search: any, replaceValue: any): string {
64 | this.postProcess = true;
65 | return this as any;
66 | }
67 | }
68 |
69 | /**
70 | * Finishes initialization of $localize, loads translations in memory and sets
71 | * the `LOCALE_ID` value.
72 | * Use this **only** if you're not using any of the two functions
73 | * `getTranslations` or `fetchTranslations`.
74 | */
75 | export function loadTranslations(
76 | parsedTranslationBundle?: ParsedTranslationBundle
77 | ) {
78 | // Restore $localize
79 | _global.$localize = backUpLocalize;
80 | if (
81 | parsedTranslationBundle?.translations &&
82 | Object.keys(parsedTranslationBundle.translations).length
83 | ) {
84 | _loadTranslations(parsedTranslationBundle.translations);
85 | }
86 | if (parsedTranslationBundle?.locale) {
87 | _global.$localize.locale = parsedTranslationBundle.locale;
88 | }
89 | isLoaded = true;
90 | }
91 |
--------------------------------------------------------------------------------
/apps/web/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
22 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
23 |
24 | /**
25 | * Web Animations `@angular/platform-browser/animations`
26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
28 | */
29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
30 |
31 | /**
32 | * By default, zone.js will patch all possible macroTask and DomEvents
33 | * user can disable parts of macroTask/DomEvents patch by setting following flags
34 | * because those flags need to be set before `zone.js` being loaded, and webpack
35 | * will put import in the top of bundle, so user need to create a separate file
36 | * in this directory (for example: zone-flags.ts), and put the following flags
37 | * into that file, and then add the following code before importing zone.js.
38 | * import './zone-flags.ts';
39 | *
40 | * The flags allowed in zone-flags.ts are listed here.
41 | *
42 | * The following flags will work for all browsers.
43 | *
44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
47 | *
48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
50 | *
51 | * (window as any).__Zone_enable_cross_context_check = true;
52 | *
53 | */
54 |
55 | /***************************************************************************************************
56 | * Zone JS is required by default for Angular itself.
57 | */
58 | import 'zone.js/dist/zone'; // Included with Angular CLI.
59 |
60 | /***************************************************************************************************
61 | * APPLICATION IMPORTS
62 | */
--------------------------------------------------------------------------------
/apps/demo/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
22 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
23 |
24 | /**
25 | * Web Animations `@angular/platform-browser/animations`
26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
28 | */
29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
30 |
31 | /**
32 | * By default, zone.js will patch all possible macroTask and DomEvents
33 | * user can disable parts of macroTask/DomEvents patch by setting following flags
34 | * because those flags need to be set before `zone.js` being loaded, and webpack
35 | * will put import in the top of bundle, so user need to create a separate file
36 | * in this directory (for example: zone-flags.ts), and put the following flags
37 | * into that file, and then add the following code before importing zone.js.
38 | * import './zone-flags.ts';
39 | *
40 | * The flags allowed in zone-flags.ts are listed here.
41 | *
42 | * The following flags will work for all browsers.
43 | *
44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
47 | *
48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
50 | *
51 | * (window as any).__Zone_enable_cross_context_check = true;
52 | *
53 | */
54 |
55 | /***************************************************************************************************
56 | * Zone JS is required by default for Angular itself.
57 | */
58 | import 'zone.js/dist/zone'; // Included with Angular CLI.
59 |
60 | /***************************************************************************************************
61 | * APPLICATION IMPORTS
62 | */
63 |
--------------------------------------------------------------------------------
/libs/app/src/web/scss/_spacing.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Convenient spacing classes
3 | */
4 | $spacer: 5 !default;
5 | $spacer-x: $spacer !default;
6 | $spacer-y: $spacer !default;
7 | $spacer-alt: 4 !default;
8 | $spacer-x-alt: $spacer-alt !default;
9 | $spacer-y-alt: $spacer-alt !default;
10 | $spacers: (
11 | 0: (
12 | x: 0,
13 | y: 0
14 | ),
15 | 2: (
16 | x: 2,
17 | y: 2
18 | ),
19 | 4: (
20 | x: $spacer-x-alt,
21 | y: $spacer-y-alt
22 | ),
23 | 5: (
24 | x: $spacer-x,
25 | y: $spacer-y
26 | ),
27 | 8: (
28 | x: $spacer-x-alt * 2,
29 | y: $spacer-y-alt * 2
30 | ),
31 | 10: (
32 | x: (
33 | $spacer-x * 2
34 | ),
35 | y: (
36 | $spacer-y * 2
37 | )
38 | ),
39 | 12: (
40 | x: $spacer-x-alt * 3,
41 | y: $spacer-y-alt * 3
42 | ),
43 | 15: (
44 | x: (
45 | $spacer-x * 3
46 | ),
47 | y: (
48 | $spacer-y * 3
49 | )
50 | ),
51 | 16: (
52 | x: $spacer-x-alt * 4,
53 | y: $spacer-y-alt * 4
54 | ),
55 | 20: (
56 | x: (
57 | $spacer-x * 4
58 | ),
59 | y: (
60 | $spacer-y * 4
61 | )
62 | ),
63 | 24: (
64 | x: $spacer-x-alt * 6,
65 | y: $spacer-y-alt * 6
66 | ),
67 | 25: (
68 | x: (
69 | $spacer-x * 5
70 | ),
71 | y: (
72 | $spacer-y * 5
73 | )
74 | ),
75 | 28: (
76 | x: $spacer-x-alt * 7,
77 | y: $spacer-y-alt * 7
78 | ),
79 | 30: (
80 | x: (
81 | $spacer-x * 6
82 | ),
83 | y: (
84 | $spacer-y * 6
85 | )
86 | )
87 | ) !default;
88 |
89 | /**
90 | * Margin and Padding
91 | * The following creates this pattern:
92 | * .m-0{margin:0}.m-t-0{margin-top:0}.m-r-0{margin-right:0}.m-b-0{margin-bottom:0}.m-l-0{margin-left:0}.m-x-0{margin-right:0;margin-left:0}.m-y-0{margin-top:0;margin-bottom:0}
93 | * Same for Padding (using the 'p' abbreviation)
94 | * From 0, 2, 5, 10, 15, 20, 25, 30
95 | **/
96 | // sass-lint:disable-all
97 | @each $prop, $abbrev in (margin: m, padding: p) {
98 | // sass-lint:enable-all
99 | @each $size, $lengths in $spacers {
100 | $length-x: map-get($lengths, x);
101 | $length-y: map-get($lengths, y);
102 |
103 | // sass-lint:disable-all
104 | .#{$abbrev}-#{$size} {
105 | #{$prop}: #{$length-y}px;
106 | } // a = All sides (can just use one length)
107 | // sass-lint:enable-all
108 | .#{$abbrev}-t-#{$size} {
109 | #{$prop}-top: #{$length-y}px;
110 | }
111 | .#{$abbrev}-r-#{$size} {
112 | #{$prop}-right: #{$length-x}px;
113 | }
114 | .#{$abbrev}-b-#{$size} {
115 | #{$prop}-bottom: #{$length-y}px;
116 | }
117 | .#{$abbrev}-l-#{$size} {
118 | #{$prop}-left: #{$length-x}px;
119 | }
120 |
121 | // Axes
122 | .#{$abbrev}-x-#{$size} {
123 | #{$prop}-right: #{$length-x}px;
124 | #{$prop}-left: #{$length-x}px;
125 | }
126 |
127 | .#{$abbrev}-y-#{$size} {
128 | #{$prop}-top: #{$length-y}px;
129 | #{$prop}-bottom: #{$length-y}px;
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/libs/cli/src/cmds/convert/translation_parsers/simple_json_translation_parser.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright Google Inc. All Rights Reserved.
4 | *
5 | * Use of this source code is governed by an MIT-style license that can be
6 | * found in the LICENSE file at https://angular.io/license
7 | */
8 | import {
9 | ɵMessageId,
10 | ɵParsedTranslation,
11 | ɵparseTranslation,
12 | } from '@angular/localize';
13 | import { Diagnostics } from '../../common/diagnostics';
14 | import { extname } from 'path';
15 | import {
16 | ParseAnalysis,
17 | ParsedTranslationBundle,
18 | TranslationParser,
19 | } from './translation_parser';
20 |
21 | /**
22 | * A translation parser that can parse JSON that has the form:
23 | *
24 | * ```
25 | * {
26 | * "locale": "...",
27 | * "translations": {
28 | * "message-id": "Target message string",
29 | * ...
30 | * }
31 | * }
32 | * ```
33 | */
34 | export class SimpleJsonTranslationParser implements TranslationParser