├── .circleci
└── config.yml
├── .github
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── package.json
├── packages
└── @ngx-i18n-router
│ ├── config-loader
│ ├── README.md
│ ├── index.ts
│ ├── package.json
│ ├── src
│ │ └── i18n-router.config-loader.ts
│ ├── tests
│ │ └── i18n-router.config-loader.spec.ts
│ ├── tsconfig.es2015.json
│ └── tsconfig.es5.json
│ ├── core
│ ├── README.md
│ ├── index.ts
│ ├── package.json
│ ├── src
│ │ ├── i18n-router.loader.ts
│ │ ├── i18n-router.pipe.ts
│ │ ├── i18n-router.service.ts
│ │ └── models
│ │ │ └── i18n-router-settings.ts
│ ├── tests
│ │ ├── common.ts
│ │ ├── i18n-router.loader.spec.ts
│ │ ├── i18n-router.pipe.spec.ts
│ │ └── i18n-router.service.spec.ts
│ ├── tsconfig.es2015.json
│ └── tsconfig.es5.json
│ └── http-loader
│ ├── README.md
│ ├── index.ts
│ ├── package.json
│ ├── src
│ └── i18n-router.http-loader.ts
│ ├── tests
│ └── i18n-router.http-loader.spec.ts
│ ├── tsconfig.es2015.json
│ └── tsconfig.es5.json
├── tools
├── build
│ ├── build-config.json
│ ├── helpers.ts
│ ├── inline-resources.ts
│ └── rollup.ts
├── test
│ └── jest.setup.ts
├── tslint.json
└── typings.d.ts
├── tsconfig.json
├── tslint.json
└── yarn.lock
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | docker:
5 | - image: circleci/node:7
6 | steps:
7 | - checkout
8 | - run: sudo npm install -g yarn@0
9 | - run: sudo yarn global add greenkeeper-lockfile@1
10 | - restore_cache:
11 | keys:
12 | - deps-{{ .Branch }}-{{ checksum "yarn.lock" }}
13 | - deps-
14 | - run: yarn
15 | - save_cache:
16 | key: deps-{{ .Branch }}-{{ checksum "yarn.lock" }}
17 | paths: 'node_modules'
18 | - run: yarn ci:before
19 | - run: yarn test:ci
20 | - run: yarn build
21 | - run: yarn ci:after
22 | - run: bash <(curl -s https://codecov.io/bash)
23 | - store_artifacts:
24 | path: coverage
25 | prefix: coverage
26 | - store_artifacts:
27 | path: dist
28 | prefix: dist
29 | - store_test_results:
30 | path: test-report.xml
31 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
6 |
7 | **I'm submitting a ...** (check one with "x")
8 | ```
9 | [ ] Regression (a behavior that used to work and stopped working in a new release)
10 | [ ] Bug report
11 | [ ] Support request =>
12 | [ ] Feature request
13 | [ ] Documentation issue or request
14 | ```
15 |
16 | **Current behavior**
17 |
18 |
19 | **Expected/desired behavior**
20 |
21 |
22 | **Minimal reproduction of the problem with instructions**
23 |
27 |
28 | **What is the motivation / use case for changing the behavior?**
29 |
30 |
31 | **Environment**
32 | * **Angular version:** X.Y.Z
33 |
34 |
35 | * **Browser:**
36 | - [ ] Chrome (desktop) version XX
37 | - [ ] Chrome (Android) version XX
38 | - [ ] Chrome (iOS) version XX
39 | - [ ] Firefox version XX
40 | - [ ] Safari (desktop) version XX
41 | - [ ] Safari (iOS) version XX
42 | - [ ] IE version XX
43 | - [ ] Edge version XX
44 |
45 | * **For Tooling issues:**
46 | - Node version: XX
47 | - Platform:
48 |
49 | * Others:
50 |
51 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ** PR Checklist
2 | Please check if your PR fulfills the following requirements:
3 |
4 | - [ ] The commit message follows our guidelines: https://github.com/fulls1z3/ngx-i18n-router/blob/master/CONTRIBUTING.md#commit
5 | - [ ] Tests for the changes have been added (for bug fixes / features)
6 | - [ ] Docs have been added / updated (for bug fixes / features)
7 |
8 | ** PR Type
9 | What kind of change does this PR introduce?
10 |
11 |
12 | ```
13 | [ ] Bugfix
14 | [ ] Feature
15 | [ ] Code style update (formatting, local variables)
16 | [ ] Refactoring (no functional changes, no api changes)
17 | [ ] Build related changes
18 | [ ] CI related changes
19 | [ ] Documentation content changes
20 | [ ] Other... Please describe:
21 | ```
22 |
23 | ** What is the current behavior?
24 |
25 |
26 | Issue Number: N/A
27 |
28 | ** What is the new behavior?
29 |
30 | ** Does this PR introduce a breaking change?
31 | ```
32 | [ ] Yes
33 | [ ] No
34 | ```
35 |
36 |
37 |
38 | ** Other information
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | .temp/
5 | dist/
6 |
7 | # dependencies
8 | node_modules/
9 |
10 | # IDEs and editors
11 | .idea/
12 |
13 | # build tools
14 | coverage/
15 | test-report.xml
16 |
17 | # misc
18 | npm-debug.log
19 | yarn-error.log
20 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 |
6 | # [5.0.0](https://github.com/fulls1z3/ngx-i18n-router/compare/v4.0.1...v5.0.0) (2018-01-07)
7 |
8 |
9 | ### Features
10 |
11 | * upgrade to angular 5 ([#83](https://github.com/fulls1z3/ngx-i18n-router/issues/83)) ([c634b90](https://github.com/fulls1z3/ngx-i18n-router/commit/c634b90))
12 |
13 |
14 |
15 |
16 | ## [4.0.1](https://github.com/fulls1z3/ngx-i18n-router/compare/v4.0.0...v4.0.1) (2017-09-07)
17 |
18 |
19 | ### Bug Fixes
20 |
21 | * fix moduleId for AoT compilation ([#43](https://github.com/fulls1z3/ngx-i18n-router/issues/43)) ([c29f01d](https://github.com/fulls1z3/ngx-i18n-router/commit/c29f01d)), closes [#41](https://github.com/fulls1z3/ngx-i18n-router/issues/41)
22 |
23 |
24 |
25 |
26 | # 4.0.0 (2017-09-07)
27 |
28 |
29 | ### Bug Fixes
30 |
31 | * depend on Angular 2.0.0 ([#3](https://github.com/fulls1z3/ngx-i18n-router/issues/3)) ([6e6ce77](https://github.com/fulls1z3/ngx-i18n-router/commit/6e6ce77))
32 | * **core:** fix improper route translations on pipe ([#31](https://github.com/fulls1z3/ngx-i18n-router/issues/31)) ([7df5381](https://github.com/fulls1z3/ngx-i18n-router/commit/7df5381)), closes [#10](https://github.com/fulls1z3/ngx-i18n-router/issues/10)
33 | * **core:** workaround for AoT compilation ([75db69b](https://github.com/fulls1z3/ngx-i18n-router/commit/75db69b))
34 |
35 |
36 | ### Features
37 |
38 | * **http-loader:** add http-loader ([7785b73](https://github.com/fulls1z3/ngx-i18n-router/commit/7785b73))
39 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mail@buraktasci.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to ngx-i18n-router
2 |
3 | We would love for you to contribute to **`ngx-i18n-router`** and help make it even better than it is today! As a contributor,
4 | here are the guidelines we would like you to follow:
5 |
6 | - [Code of Conduct](#coc)
7 | - [Issues and Bugs](#issue)
8 | - [Feature requests](#feature)
9 | - [Submission guidelines](#submit)
10 | - [Coding rules](#rules)
11 | - [Commit message guidelines](#commit)
12 |
13 | ## Code of Conduct
14 | Help us keep **`ngx-i18n-router`** open and inclusive. Please read and follow our [Code of Conduct][coc].
15 |
16 | ## Found a Bug?
17 | If you find a bug in the source code, you can help us by [submitting an issue](#submit-issue) to our [GitHub Repository][github].
18 |
19 | Even better, you can [submit a Pull Request](#submit-pr) with a fix.
20 |
21 | ## Missing a Feature?
22 | You can *request* a new feature by [submitting an issue](#submit-issue) to our GitHub Repository.
23 |
24 | If you would like to *implement* a new feature, please submit an issue with a proposal for your work first, to be sure that
25 | we can use it.
26 |
27 | Please consider what kind of change it is:
28 | * For a **Major Feature**, first open an issue and outline your proposal so that it can be discussed.
29 | This will also allow us to better coordinate our efforts, prevent duplication of work, and help you to craft the change
30 | so that it is successfully accepted into the project.
31 | * **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
32 |
33 | ## Submission guidelines
34 | ### Submitting an Issue
35 | Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion
36 | might inform you of workarounds readily available.
37 |
38 | We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order
39 | to reproduce bugs we will systematically ask you to provide a minimal reproduction scenario using http://plnkr.co.
40 |
41 | Having a live, reproducible scenario gives us wealth of important information without going back & forth to you with additional
42 | questions like:
43 | - version used
44 | - 3rd-party libraries and their versions
45 | - and most importantly: a use-case that fails
46 |
47 | A minimal reproduce scenario using http://plnkr.co/ allows us to quickly confirm a bug (or point out coding problem) as
48 | well as confirm that we are fixing the right problem. If plunker is not a suitable way to demonstrate the problem (*ex:
49 | issues related to our npm packaging*), please create a standalone git repository demonstrating the problem.
50 |
51 | We will be insisting on a minimal reproduce scenario in order to save maintainers time and ultimately be able to fix more
52 | bugs.
53 |
54 | Interestingly, from our experience users often find coding problems themselves while preparing a minimal plunk. We understand
55 | that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate
56 | the problem before we can fix it.
57 |
58 | Unfortunately we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you,
59 | we are going to close an issue that don't have enough info to be reproduced.
60 |
61 | You can file new issues by filling out our [new issue form](https://github.com/fulls1z3/ngx-i18n-router/issues/new).
62 |
63 | ### Submitting a Pull Request (PR)
64 | Before you submit your Pull Request (PR) consider the following guidelines:
65 |
66 | * Search [GitHub](https://github.com/fulls1z3/ngx-i18n-router/pulls) for an open or closed PR that relates to your submission.
67 | You don't want to duplicate effort.
68 | * Make your changes in a new git branch:
69 | ```shell
70 | git checkout -b my-fix-branch master
71 | ```
72 | * Create your patch, **including appropriate test cases**.
73 | * Follow our [Coding rules](#rules).
74 | * Run the full test suite and ensure that all tests pass.
75 | * Commit your changes using a descriptive commit message that follows our [commit message conventions](#commit).
76 | Adherence to these conventions is necessary because release notes are automatically generated from these messages.
77 | ```shell
78 | git commit -a
79 | ```
80 | Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
81 | * Push your branch to GitHub:
82 | ```shell
83 | git push origin my-fix-branch
84 | ```
85 | * In GitHub, send a pull request to `ngx-i18n-router:master`.
86 | * If we suggest changes then:
87 | * Make the required updates.
88 | * Re-run the test suites to ensure tests are still passing.
89 | * Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
90 | ```shell
91 | git rebase master -i
92 | git push -f
93 | ```
94 | That's it, thanks for your contribution!
95 |
96 | #### After your pull request is merged
97 | After your pull request is merged, you can safely delete your branch and pull the changes from the main (upstream) repository:
98 | * Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
99 | ```shell
100 | git push origin --delete my-fix-branch
101 | ```
102 | * Check out the master branch:
103 | ```shell
104 | git checkout master -f
105 | ```
106 | * Delete the local branch:
107 | ```shell
108 | git branch -D my-fix-branch
109 | ```
110 | * Update your master with the latest upstream version:
111 | ```shell
112 | git pull --ff upstream master
113 | ```
114 |
115 | ## Coding rules
116 | To ensure consistency throughout the source code, keep these rules in mind as you are working:
117 | * All features or bug fixes **must be tested** by one or more specs (unit-tests).
118 | * All public API methods **must be documented**. (Details TBC).
119 | * We follow [fulls1z3's Angular TSLint rules][angular-tslint-rules].
120 |
121 | ## Commit message guidelines
122 | We have very precise rules over how our git commit messages can be formatted. This leads to **more readable messages** that
123 | are easy to follow when looking through the **project history**. But also, we use the git commit messages to **generate
124 | the `ngx-i18n-router` change log**.
125 |
126 | ### Commit Message Format
127 | Each commit message consists of a **header**, a **body** and a **footer**. The header has a special format that includes
128 | a **type**, a **scope** (*when applicable*) and a **subject**:
129 | ```
130 | ():
131 |
132 |
133 |
134 |
135 | ```
136 |
137 | The **header** is mandatory and the **scope** of the header is optional.
138 |
139 | Any line of the commit message cannot be longer 100 characters. This allows the message to be easier to read on GitHub as
140 | well as in various git tools.
141 |
142 | Footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/)
143 | if any.
144 |
145 | Samples: (even more [samples](https://github.com/fulls1z3/ngx-i18n-router/commits/master))
146 | ```
147 | docs(changelog): update change log to alpha.4
148 | ```
149 | ```
150 | fix(release): need to depend on latest rxjs and zone.js
151 |
152 | The version in our package.json gets copied to the one we publish, and users need the latest of these.
153 | ```
154 |
155 | ### Revert
156 | If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit.
157 | In the body it should say: `This reverts commit .`, where the hash is the SHA of the commit being reverted.
158 |
159 | ### Type
160 | Must be one of the following:
161 | * **build**: Changes that affect the build system or external dependencies (example scopes: gulp, npm, webpack)
162 | * **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, etc)
163 | * **docs**: Documentation only changes
164 | * **feat**: A new feature
165 | * **fix**: A bug fix
166 | * **perf**: A code change that improves performance
167 | * **refactor**: A code change that neither fixes a bug nor adds a feature
168 | * **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
169 | * **test**: Adding missing tests or correcting existing tests
170 |
171 | ### Scope
172 | The scope should be the name of the project affected.
173 |
174 | The following is the list of supported scopes:
175 | * **core**
176 | * **http-loader**
177 | * **config-loader**
178 |
179 | There are currently a few exceptions to the "use project name" rule:
180 |
181 | * **packaging**: used for changes that change the package layout (*e.g. package.json, bundles, path changes, etc.*)
182 | * **changelog**: used for updating the release notes in CHANGELOG.md
183 |
184 | ### Subject
185 | The subject contains succinct description of the change:
186 | * use the imperative, present tense: "change" not "changed" nor "changes"
187 | * don't capitalize first letter
188 | * no dot (.) at the end
189 |
190 | ### Body
191 | Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes". The body should include
192 | the motivation for the change and contrast this with previous behavior.
193 |
194 | ### Footer
195 | The footer should contain any information about **Breaking Changes** and is also the place to reference GitHub issues that
196 | this commit **Closes**.
197 |
198 | **Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit
199 | message is then used for this.
200 |
201 | [coc]: https://github.com/fulls1z3/ngx-i18n-router/blob/master/CODE_OF_CONDUCT.md
202 | [github]: https://github.com/fulls1z3/ngx-i18n-router
203 | [angular-tslint-rules]: https://github.com/ng-seed/angular-tslint-rules
204 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Burak Tasci
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ngx-i18n-router
2 | Route internationalization utility for **Angular**
3 |
4 | [](https://circleci.com/gh/fulls1z3/ngx-i18n-router)
5 | [](https://codecov.io/gh/fulls1z3/ngx-i18n-router)
6 | [](https://github.com/facebook/jest)
7 | [](https://conventionalcommits.org)
8 | [](https://greenkeeper.io/)
9 | [](https://angular.io/styleguide)
10 |
11 | > Please support this project by simply putting a Github star. Share this library with friends on Twitter and everywhere else you can.
12 |
13 | **`ngx-i18n-router`** translates each `path` and `redirectTo` property of routes, during **Angular** app initialization
14 | and also during runtime - when the working language gets changed.
15 |
16 | #### NOTICE
17 | > This *[5.x.x] branch* is intented to work with `@angular v5.x.x`. If you're developing on a later release of **Angular**
18 | than `v5.x.x`, then you should probably choose the appropriate version of this library by visiting the *[master] branch*.
19 |
20 | > Also, please check the [Workaround for '@ngtools/webpack'](https://github.com/fulls1z3/ngx-i18n-router/tree/master/packages/@ngx-i18n-router/core/README.md#workaround-for-ngtoolswebpack)
21 | section if your app depends on **@angular-cli** or **`@ngtools/webpack`** for [AoT compilation].
22 |
23 | ## Packages:
24 | Name | Description | NPM
25 | --- | --- | ---
26 | [@ngx-i18n-router/core](https://github.com/fulls1z3/ngx-i18n-router/tree/master/packages/@ngx-i18n-router/core) | Route internationalization utility for **Angular** | [](https://www.npmjs.com/package/@ngx-i18n-router/core)
27 | [@ngx-i18n-router/http-loader](https://github.com/fulls1z3/ngx-i18n-router/tree/master/packages/@ngx-i18n-router/http-loader) | Loader for [ngx-i18n-router] that provides route translations using `http` | [](https://www.npmjs.com/package/@ngx-i18n-router/http-loader)
28 | [@ngx-i18n-router/config-loader](https://github.com/fulls1z3/ngx-i18n-router/tree/master/packages/@ngx-i18n-router/config-loader) | Loader for [ngx-i18n-router] that provides route translations using [ngx-config] | [](https://www.npmjs.com/package/@ngx-i18n-router/config-loader)
29 |
30 | ### Examples
31 | - [ng-seed/universal] and [fulls1z3/example-app] are officially maintained projects, showcasing common patterns and best
32 | practices for **`ngx-i18n-router`**.
33 |
34 | ## Contributing
35 | If you want to file a bug, contribute some code, or improve documentation, please read up on the following contribution guidelines:
36 | - [Issue guidelines](CONTRIBUTING.md#submit)
37 | - [Contributing guidelines](CONTRIBUTING.md)
38 | - [Coding rules](CONTRIBUTING.md#rules)
39 | - [Change log](CHANGELOG.md)
40 |
41 | #### Thanks to
42 | - [JetBrains], for their support to this open source project with free [WebStorm] licenses.
43 |
44 | ## License
45 | The MIT License (MIT)
46 |
47 | Copyright (c) 2018 [Burak Tasci]
48 |
49 | [master]: https://github.com/ngx-i18n-router/core/tree/master
50 | [5.x.x]: https://github.com/ngx-i18n-router/core/tree/5.x.x
51 | [AoT compilation]: https://angular.io/docs/ts/latest/cookbook/aot-compiler.html
52 | [ngx-i18n-router]: https://github.com/fulls1z3/ngx-i18n-router
53 | [ngx-config]: https://github.com/fulls1z3/ngx-config
54 | [ng-seed/universal]: https://github.com/ng-seed/universal
55 | [fulls1z3/example-app]: https://github.com/fulls1z3/example-app
56 | [JetBrains]: https://www.jetbrains.com/community/opensource
57 | [WebStorm]: https://www.jetbrains.com/webstorm
58 | [Burak Tasci]: https://github.com/fulls1z3
59 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ngx-i18n-router",
3 | "version": "5.0.0",
4 | "description": "Route internationalization utility for Angular",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/fulls1z3/ngx-i18n-router.git"
8 | },
9 | "keywords": [],
10 | "author": {
11 | "name": "Burak Tasci",
12 | "email": "me@fulls1z3.com"
13 | },
14 | "license": "MIT",
15 | "bugs": {
16 | "url": "https://github.com/fulls1z3/ngx-i18n-router/issues"
17 | },
18 | "homepage": "https://github.com/fulls1z3/ngx-i18n-router#readme",
19 | "scripts": {
20 | "clean": "rimraf .temp dist",
21 | "build": "ts-node ./tools/build/rollup.ts && rimraf .temp",
22 | "lint": "tslint -p ./tsconfig.json --force",
23 | "rebuild": "npm run clean && npm run build",
24 | "ci:before": "greenkeeper-lockfile-update",
25 | "ci:after": "greenkeeper-lockfile-upload",
26 | "test": "jest --runInBand --colors",
27 | "test:ci": "jest --ci --updateSnapshot --colors",
28 | "release": "standard-version"
29 | },
30 | "devDependencies": {
31 | "@angular/common": "5.2.1",
32 | "@angular/compiler": "5.2.1",
33 | "@angular/compiler-cli": "5.2.1",
34 | "@angular/core": "5.2.1",
35 | "@angular/platform-browser": "5.2.1",
36 | "@angular/platform-browser-dynamic": "5.2.1",
37 | "@angular/router": "5.2.1",
38 | "core-js": "~2.5.3",
39 | "rxjs": "~5.5.6",
40 | "zone.js": "~0.8.20",
41 | "request": "~2.83.0",
42 | "lodash": "~4.17.4",
43 | "@ngx-config/core": "5.0.0",
44 | "@ngx-config/http-loader": "5.0.0",
45 | "@types/node": "~9.4.0",
46 | "@types/jest": "~22.1.0",
47 | "@types/lodash": "4.14.55",
48 | "rimraf": "~2.6.2",
49 | "ts-node": "~4.1.0",
50 | "glob": "~7.1.2",
51 | "camelcase": "~4.1.0",
52 | "rollup": "~0.42.0",
53 | "rollup-plugin-node-resolve": "~3.0.2",
54 | "rollup-plugin-commonjs": "~8.3.0",
55 | "rollup-plugin-sourcemaps": "~0.4.2",
56 | "rollup-plugin-uglify": "~3.0.0",
57 | "reflect-metadata": "~0.1.12",
58 | "tsickle": "~0.26.0",
59 | "jest": "~22.1.1",
60 | "jest-preset-angular": "~5.0.0",
61 | "jest-junit-reporter": "~1.1.0",
62 | "standard-version": "~4.3.0",
63 | "codelyzer": "~4.1.0",
64 | "tslint": "~5.9.1",
65 | "angular-tslint-rules": "1.3.0",
66 | "typescript": "~2.6.2"
67 | },
68 | "jest": {
69 | "preset": "jest-preset-angular",
70 | "setupTestFrameworkScriptFile": "./tools/test/jest.setup.ts",
71 | "testResultsProcessor": "./node_modules/jest-junit-reporter",
72 | "globals": {
73 | "ts-jest": {
74 | "tsConfigFile": "./tsconfig.json"
75 | },
76 | "__TRANSFORM_HTML__": true
77 | },
78 | "moduleDirectories": [
79 | "node_modules",
80 | "packages"
81 | ],
82 | "moduleNameMapper": null,
83 | "cache": false,
84 | "silent": true,
85 | "collectCoverage": true,
86 | "collectCoverageFrom": [
87 | "packages/@ngx-i18n-router/core/src/**/**.ts",
88 | "packages/@ngx-i18n-router/http-loader/src/**/**.ts",
89 | "packages/@ngx-i18n-router/config-loader/src/**/**.ts"
90 | ]
91 | },
92 | "greenkeeper": {
93 | "ignore": [
94 | "@types/lodash",
95 | "rollup"
96 | ]
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/config-loader/README.md:
--------------------------------------------------------------------------------
1 | # @ngx-i18n-router/config-loader [](https://www.npmjs.com/package/@ngx-i18n-router/config-loader) [](https://www.npmjs.com/package/@ngx-i18n-router/config-loader)
2 | Loader for [ngx-i18n-router] that provides route translations using [ngx-config]
3 |
4 | [](https://circleci.com/gh/fulls1z3/ngx-i18n-router)
5 | [](https://codecov.io/gh/fulls1z3/ngx-i18n-router)
6 | [](https://github.com/facebook/jest)
7 | [](https://conventionalcommits.org)
8 | [](https://angular.io/styleguide)
9 |
10 | > Please support this project by simply putting a Github star. Share this library with friends on Twitter and everywhere else you can.
11 |
12 | **`@ngx-i18n-router/config-loader`** provides **route translations** to [@ngx-i18n-router/core] using [@ngx-config/core],
13 | and helps **reducing** the **amount** of `HTTP` requests during application initalization by including **route translations**
14 | within the **application settings** - if [@ngx-config/core] is already used to retrieve settings by the **Angular** app.
15 |
16 | #### NOTICE
17 | > This *[5.x.x] branch* is intented to work with `@angular v5.x.x`. If you're developing on a later release of **Angular**
18 | than `v5.x.x`, then you should probably choose the appropriate version of this library by visiting the *[master] branch*.
19 |
20 | ## Table of contents:
21 | - [Prerequisites](#prerequisites)
22 | - [Getting started](#getting-started)
23 | - [Installation](#installation)
24 | - [Examples](#examples)
25 | - [Related packages](#related-packages)
26 | - [Adding `@ngx-i18n-router/config-loader` to your project (SystemJS)](#adding-systemjs)
27 | - [Settings](#settings)
28 | - [Setting up `I18NRouterModule` to use `I18NRouterConfigLoader`](#setting-up-configloader)
29 | - [Translations object](#translations-object)
30 | - [License](#license)
31 |
32 | ## Prerequisites
33 | This library depends on `Angular v4.0.0`. Older versions contain outdated dependencies, might produce errors.
34 |
35 | Also, please ensure that you are using **`Typescript v2.5.3`** or higher.
36 |
37 | ## Getting started
38 | ### Installation
39 | You can install **`@ngx-i18n-router/config-loader`** using `npm`
40 | ```
41 | npm install @ngx-i18n-router/config-loader --save
42 | ```
43 |
44 | **Note**: You should have already installed [@ngx-i18n-router/core] and [@ngx-config/core].
45 |
46 | ### Examples
47 | - [ng-seed/universal] and [fulls1z3/example-app] are officially maintained projects, showcasing common patterns and best
48 | practices for **`@ngx-i18n-router/config-loader`**.
49 |
50 | ### Related packages
51 | The following packages may be used in conjunction with **`@ngx-i18n-router/config-loader`**:
52 | - [@ngx-i18n-router/core]
53 | - [@ngx-config/core]
54 |
55 | ### Adding `@ngx-i18n-router/config-loader` to your project (SystemJS)
56 | Add `map` for **`@ngx-i18n-router/config-loader`** in your `systemjs.config`
57 | ```javascript
58 | '@ngx-i18n-router/config-loader': 'node_modules/@ngx-i18n-router/config-loader/bundles/config-loader.umd.min.js'
59 | ```
60 |
61 | ## Settings
62 | ### Setting up `I18NRouterModule` to use `I18NRouterConfigLoader`
63 | `I18NRouterConfigLoader` uses [@ngx-config/core] to load route translations.
64 | - Import `ConfigModule` using the mapping `'@ngx-config/core'` and append `ConfigModule.forRoot({...})` within the imports
65 | property of **app.module**.
66 | - Import `I18NRouterModule` using the mapping `'@ngx-i18n-router/core'` and append `I18NRouterModule.forRoot({...})` within
67 | the imports property of **app.module**.
68 | - Import `I18NRouterConfigLoader` using the mapping `'@ngx-i18n-router/config-loader'`.
69 |
70 | **Note**: *Considering the app.module is the core module in Angular application*.
71 |
72 | #### app.module.ts
73 | ```TypeScript
74 | ...
75 | import { HttpClient } from '@angular/common/http';
76 | import { ConfigModule, ConfigLoader, ConfigHttpLoader } from '@ngx-config/core';
77 | import { I18NRouterModule, I18NRouterLoader, I18N_ROUTER_PROVIDERS, RAW_ROUTES } from '@ngx-i18n-router/core';
78 | import { I18NRouterConfigLoader } from '@ngx-i18n-router/config-loader';
79 | ...
80 |
81 | export function configFactory(http: HttpClient): ConfigLoader {
82 | return new ConfigHttpLoader(http, './config.json');
83 | }
84 |
85 | export function i18nRouterFactory(config: ConfigService, rawRoutes: Routes): I18NRouterLoader {
86 | return new I18NRouterConfigLoader(config, 'routes', {routes: rawRoutes});
87 | }
88 |
89 | @NgModule({
90 | declarations: [
91 | AppComponent,
92 | ...
93 | ],
94 | ...
95 | imports: [
96 | RouterModule.forRoot(routes),
97 | ConfigModule.forRoot({
98 | provide: ConfigLoader,
99 | useFactory: (configFactory),
100 | deps: [Http]
101 | }),
102 | I18NRouterModule.forRoot(routes, [
103 | {
104 | provide: I18NRouterLoader,
105 | useFactory: (i18nRouterFactory),
106 | deps: [ConfigService, RAW_ROUTES]
107 | }
108 | ]),
109 | ...
110 | ],
111 | ...
112 | providers: [
113 | I18N_ROUTER_PROVIDERS,
114 | ...
115 | ],
116 | ...
117 | bootstrap: [AppComponent]
118 | })
119 | ```
120 |
121 | `I18NRouterConfigLoader` has three parameters:
122 | - **config**: `ConfigService` : ConfigService instance
123 | - **group**: `string` : group key, to fetch route translations from applcation settings (*by default, `routes`*)
124 | - **providedSettings**: `I18NRouterSettings` : i18n-router settings
125 | - **routes**: `Routes`: raw routes
126 |
127 | ### Translations object
128 | You can find detailed information about the **data structure** and usage guidelines for the translations object [here](https://github.com/fulls1z3/ngx-i18n-router/tree/master/packages/@ngx-i18n-router/core#translations-object).
129 |
130 | Assuming that application settings are provided from `./config.json`, adding the following data to configuration file will
131 | provide route translations to **`@ngx-i18n-router/core`** through **`@ngx-config/core`**.
132 |
133 | #### config.json
134 | ```json
135 | {
136 | ...
137 | "routes": {
138 | "en": {
139 | "ROOT.ABOUT": "about",
140 | "ROOT.ABOUT.US": "us",
141 | "ROOT.ABOUT.BANANA": "banana",
142 | "ROOT.ABOUT.APPLE": "apple",
143 | "ROOT.ABOUT.APPLE.PEAR": "pear",
144 | "CHANGE_LANGUAGE": "change-language"
145 | },
146 | "tr": {
147 | "ROOT.ABOUT": "hakkinda",
148 | "ROOT.ABOUT.US": "biz",
149 | "ROOT.ABOUT.BANANA": "muz",
150 | "ROOT.ABOUT.APPLE": "elma",
151 | "ROOT.ABOUT.APPLE.PEAR": "armut",
152 | "CHANGE_LANGUAGE": "dil-secimi"
153 | }
154 | },
155 | ...
156 | }
157 | ```
158 |
159 | > :+1: Well! **`@ngx-i18n-router/config-loader`** will now provide **route translations** to [@ngx-i18n-router/core] using
160 | [@ngx-config/core].
161 |
162 | ## License
163 | The MIT License (MIT)
164 |
165 | Copyright (c) 2018 [Burak Tasci]
166 |
167 | [master]: https://github.com/ngx-i18n-router/core/tree/master
168 | [5.x.x]: https://github.com/ngx-i18n-router/core/tree/5.x.x
169 | [ngx-i18n-router]: https://github.com/fulls1z3/ngx-i18n-router
170 | [ngx-config]: https://github.com/fulls1z3/ngx-config
171 | [@ngx-i18n-router/core]: https://github.com/fulls1z3/ngx-i18n-router/tree/master/packages/@ngx-i18n-router/core
172 | [@ngx-config/core]: https://github.com/fulls1z3/ngx-config/tree/master/packages/@ngx-config/core
173 | [ng-seed/universal]: https://github.com/ng-seed/universal
174 | [fulls1z3/example-app]: https://github.com/fulls1z3/example-app
175 | [Burak Tasci]: https://github.com/fulls1z3
176 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/config-loader/index.ts:
--------------------------------------------------------------------------------
1 | export * from './src/i18n-router.config-loader';
2 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/config-loader/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ngx-i18n-router/config-loader",
3 | "version": "5.0.0",
4 | "description": "Loader for ngx-i18n-router that provides route translations using ngx-config",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/fulls1z3/ngx-i18n-router.git"
8 | },
9 | "keywords": [
10 | "lazy loading",
11 | "localization",
12 | "i18n",
13 | "async",
14 | "plugin",
15 | "loader",
16 | "configmodule",
17 | "config",
18 | "localized router",
19 | "router",
20 | "i18n router",
21 | "angular"
22 | ],
23 | "author": {
24 | "name": "Burak Tasci",
25 | "email": "me@fulls1z3.com"
26 | },
27 | "license": "MIT",
28 | "bugs": {
29 | "url": "https://github.com/fulls1z3/ngx-i18n-router/issues"
30 | },
31 | "homepage": "https://github.com/fulls1z3/ngx-i18n-router/tree/master/packages/@ngx-i18n-router/config-loader#readme",
32 | "main": "./bundles/config-loader.umd.js",
33 | "module": "./@ngx-i18n-router/config-loader.es5.js",
34 | "es2015": "./@ngx-i18n-router/config-loader.js",
35 | "typings": "./config-loader.d.ts",
36 | "dependencies": {
37 | "tslib": "~1.8.1"
38 | },
39 | "peerDependencies": {
40 | "@angular/common": ">=5.0.0 <6.0.0",
41 | "@angular/core": ">=5.0.0 <6.0.0",
42 | "@angular/platform-browser-dynamic": ">=5.0.0 <6.0.0",
43 | "@angular/router": ">=5.0.0 <6.0.0",
44 | "lodash": ">=4.17.4",
45 | "@ngx-config/core": ">=5.0.0 <6.0.0",
46 | "@ngx-config/http-loader": ">=5.0.0 <6.0.0",
47 | "@ngx-i18n-router/core": ">=5.0.0 <6.0.0"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/config-loader/src/i18n-router.config-loader.ts:
--------------------------------------------------------------------------------
1 | // angular
2 | import { Routes } from '@angular/router';
3 |
4 | // libs
5 | import * as _ from 'lodash';
6 | import { ConfigService } from '@ngx-config/core';
7 | import { I18NRouterLoader, I18NRouterSettings } from '@ngx-i18n-router/core';
8 |
9 | export class I18NRouterConfigLoader implements I18NRouterLoader {
10 | get routes(): Routes {
11 | return _.map(this.providedSettings.routes, _.cloneDeep);
12 | }
13 |
14 | get translations(): any {
15 | if (!this.config)
16 | throw new Error('No [config] specified!');
17 |
18 | if (!this.config.getSettings())
19 | return undefined;
20 |
21 | return this.config.getSettings(this.group);
22 | }
23 |
24 | constructor(private readonly config: ConfigService,
25 | private readonly group: string = 'routes',
26 | private readonly providedSettings: I18NRouterSettings = {}) {
27 | }
28 |
29 | loadTranslations(): any {
30 | return Promise.resolve(undefined);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/config-loader/tests/i18n-router.config-loader.spec.ts:
--------------------------------------------------------------------------------
1 | // angular
2 | import { async, inject, TestBed } from '@angular/core/testing';
3 | import { HttpClient } from '@angular/common/http';
4 | import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
5 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
6 | import { Routes } from '@angular/router';
7 | import { RouterTestingModule } from '@angular/router/testing';
8 |
9 | // libs
10 | import { ConfigLoader, ConfigModule, ConfigService } from '@ngx-config/core';
11 | import { ConfigHttpLoader } from '@ngx-config/http-loader';
12 | import { I18N_ROUTER_PROVIDERS, I18NRouterLoader, I18NRouterModule, I18NRouterService, RAW_ROUTES } from '@ngx-i18n-router/core';
13 |
14 | // module
15 | import { I18NRouterConfigLoader } from '../index';
16 |
17 | export const testSettings = {
18 | routes: {
19 | en: {
20 | 'ROOT.ABOUT': 'about'
21 | },
22 | tr: {
23 | 'ROOT.ABOUT': 'hakkinda'
24 | }
25 | }
26 | };
27 | const testRoutes: Routes = [];
28 |
29 | const testModuleConfig = (routes: Routes = [], configOptions?: any, i18nRouterOptions?: Array) => {
30 | TestBed.resetTestEnvironment();
31 |
32 | TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting())
33 | .configureTestingModule({
34 | imports: [
35 | HttpClientTestingModule,
36 | RouterTestingModule,
37 | ConfigModule.forRoot(configOptions),
38 | I18NRouterModule.forRoot(routes, i18nRouterOptions)
39 | ],
40 | providers: [I18N_ROUTER_PROVIDERS]
41 | });
42 | };
43 |
44 | describe('@ngx-i18n-router/config-loader:',
45 | () => {
46 | beforeEach(() => {
47 | const configFactory = (http: HttpClient) => new ConfigHttpLoader(http);
48 | const i18nRouterFactory = (config: ConfigService, rawRoutes: Routes) => new I18NRouterConfigLoader(config, 'routes',
49 | {routes: rawRoutes});
50 |
51 | testModuleConfig(testRoutes,
52 | {
53 | provide: ConfigLoader,
54 | useFactory: (configFactory),
55 | deps: [HttpClient]
56 | },
57 | [{
58 | provide: I18NRouterLoader,
59 | useFactory: (i18nRouterFactory),
60 | deps: [ConfigService, RAW_ROUTES]
61 | }]
62 | );
63 | });
64 |
65 | describe('I18NRouterConfigLoader',
66 | () => {
67 | it('should throw w/o `ConfigService`',
68 | () => {
69 | const loader = new I18NRouterConfigLoader(undefined);
70 |
71 | expect(() => loader.translations).toThrowError('No [config] specified!');
72 | });
73 |
74 | it('should not return any routes & translations unless provided',
75 | inject([ConfigService],
76 | (config: ConfigService) => {
77 | const loader = new I18NRouterConfigLoader(config);
78 | const loadedRoutes = loader.routes;
79 | const loadedTranslations = loader.translations;
80 |
81 | expect(loadedRoutes).toEqual([]);
82 | expect(loadedTranslations).toBeUndefined();
83 | }));
84 |
85 | it('should be able to retrieve route translations from `ConfigService`',
86 | async(inject([ConfigService, I18NRouterService],
87 | (config: ConfigService, i18nRouter: I18NRouterService) => {
88 | config.init()
89 | .then((settings: any) => {
90 | expect(settings).toEqual(testSettings);
91 |
92 | const loadedTranslations = i18nRouter.loader.translations;
93 | expect(loadedTranslations).toEqual(testSettings['routes']);
94 | });
95 |
96 | const httpMock = TestBed.get(HttpTestingController);
97 | const reqs = httpMock.match('/config.json');
98 |
99 | for (const req of reqs)
100 | req.flush(testSettings);
101 |
102 | httpMock.verify();
103 | })));
104 | });
105 | });
106 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/config-loader/tsconfig.es2015.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../../tsconfig.json",
3 | "compilerOptions": {
4 | "target": "es2015",
5 | "module": "es2015",
6 | "noImplicitAny": true,
7 | "suppressImplicitAnyIndexErrors": true,
8 | "importHelpers": true,
9 | "sourceMap": true,
10 | "inlineSources": true,
11 | "declaration": true,
12 | "removeComments": true,
13 | "stripInternal": true,
14 | "outDir": "../../../../.temp/packages/@ngx-i18n-router/config-loader-es2015/",
15 | "rootDir": "./",
16 | "baseUrl": "",
17 | "paths": {
18 | "@ngx-i18n-router/core": ["../../../../dist/@ngx-i18n-router/core"]
19 | },
20 | "typeRoots": ["../../../../node_modules/@types"],
21 | "types": ["node"],
22 | "lib": [
23 | "es2015",
24 | "dom"
25 | ]
26 | },
27 | "include": [],
28 | "files": ["./index.ts"],
29 | "angularCompilerOptions": {
30 | "annotateForClosureCompiler": true,
31 | "strictMetadataEmit": true,
32 | "skipTemplateCodegen": true,
33 | "flatModuleOutFile": "config-loader.js",
34 | "flatModuleId": "@ngx-i18n-router/config-loader",
35 | "genDir": "../../../../.temp/gen-dir/"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/config-loader/tsconfig.es5.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.es2015.json",
3 | "compilerOptions": {
4 | "target": "es5",
5 | "outDir": "../../../../.temp/packages/@ngx-i18n-router/config-loader-es5/"
6 | },
7 | "files": ["./index.ts"],
8 | "angularCompilerOptions": {
9 | "annotateForClosureCompiler": true,
10 | "strictMetadataEmit": true,
11 | "skipTemplateCodegen": true,
12 | "flatModuleOutFile": "config-loader.js",
13 | "flatModuleId": "@ngx-i18n-router/config-loader",
14 | "genDir": "../../../../.temp/gen-dir/"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/core/README.md:
--------------------------------------------------------------------------------
1 | # @ngx-i18n-router/core [](https://www.npmjs.com/package/@ngx-i18n-router/core) [](https://www.npmjs.com/package/@ngx-i18n-router/core)
2 | Route internationalization utility for **Angular**
3 |
4 | [](https://circleci.com/gh/fulls1z3/ngx-i18n-router)
5 | [](https://codecov.io/gh/fulls1z3/ngx-i18n-router)
6 | [](https://github.com/facebook/jest)
7 | [](https://conventionalcommits.org)
8 | [](https://angular.io/styleguide)
9 |
10 | > Please support this project by simply putting a Github star. Share this library with friends on Twitter and everywhere else you can.
11 |
12 | **`@ngx-i18n-router/core`** translates each `path` and `redirectTo` property of routes, during **Angular** app initialization
13 | and also during runtime - when the working language gets changed.
14 |
15 | #### NOTICE
16 | > This *[5.x.x] branch* is intented to work with `@angular v5.x.x`. If you're developing on a later release of **Angular**
17 | than `v5.x.x`, then you should probably choose the appropriate version of this library by visiting the *[master] branch*.
18 |
19 | > Also, please check the [Workaround for '@ngtools/webpack'](#workaround-for-ngtoolswebpack) section if your app depends
20 | on **@angular/cli** or **`@ngtools/webpack`** for [AoT compilation].
21 |
22 | ## Table of contents:
23 | - [Prerequisites](#prerequisites)
24 | - [Getting started](#getting-started)
25 | - [Installation](#installation)
26 | - [Examples](#examples)
27 | - [Related packages](#related-packages)
28 | - [Recommended packages](#recommended-packages)
29 | - [Adding `@ngx-i18n-router/core` to your project (SystemJS)](#adding-systemjs)
30 | - [Route configuration](#route-config)
31 | - [app.module configuration](#appmodule-config)
32 | - [Feature modules configuration](#feature-modules-config)
33 | - [app.component configuration](#appcomponent-config)
34 | - [Settings](#settings)
35 | - [Setting up `I18NRouterModule` to use `I18NRouterStaticLoader`](#setting-up-staticloader)
36 | - [Setting up `I18NRouterModule` to use `I18NRouterHttpLoader`](#setting-up-httploader)
37 | - [Setting up `I18NRouterModule` to use `I18NRouterConfigLoader`](#setting-up-configloader)
38 | - [Translations object](#translations-object)
39 | - [Change language (runtime)](#change-language)
40 | - [Pipe](#pipe)
41 | - [Workaround for '@ngtools/webpack'](#workaround-for-ngtoolswebpack)
42 | - [Credits](#credits)
43 | - [License](#license)
44 |
45 | ## Prerequisites
46 | This library depends on `Angular v4.0.0`. Older versions contain outdated dependencies, might produce errors.
47 |
48 | Also, please ensure that you are using **`Typescript v2.5.3`** or higher.
49 |
50 | ## Getting started
51 | ### Installation
52 | You can install **`@ngx-i18n-router/core`** using `npm`
53 | ```
54 | npm install @ngx-i18n-router/core --save
55 | ```
56 |
57 | ### Examples
58 | - [ng-seed/universal] and [fulls1z3/example-app] are officially maintained projects, showcasing common patterns and best
59 | practices for **`@ngx-i18n-router/core`**.
60 |
61 | ### Related packages
62 | The following packages may be used in conjunction with **`@ngx-i18n-router/core`**
63 | - [@ngx-i18n-router/http-loader]
64 | - [@ngx-i18n-router/config-loader]
65 |
66 | ### Recommended packages
67 | The following package(s) have no dependency for **`@ngx-i18n-router/core`**, however may provide supplementary/shorthand
68 | functionality:
69 | - [@ngx-config/core]: provides route translations from the application settings loaded during application initialization
70 | - [@ngx-cache/core]: provides caching features to retrieve the route translations using `non-static loaders` (`http`, `fs`,
71 | etc.)
72 |
73 | ### Adding `@ngx-i18n-router/core` to your project (SystemJS)
74 | Add `map` for **`@ngx-i18n-router/core`** in your `systemjs.config`
75 | ```javascript
76 | '@ngx-i18n-router/core': 'node_modules/@ngx-i18n-router/core/bundles/core.umd.min.js'
77 | ```
78 |
79 | ### ### Route configuration
80 | In order to use **`@ngx-i18n-router/core`** properly, you should have more or less a similar **route structure** as follows:
81 |
82 | #### app.routes.ts
83 | ```TypeScript
84 | export const routes: Routes = [
85 | {
86 | path: '',
87 | children: [
88 | {
89 | path: '',
90 | loadChildren: './+home/home.module#HomeModule'
91 | },
92 | {
93 | path: 'about',
94 | loadChildren: './+about/about.module#AboutModule'
95 | },
96 |
97 | ...
98 |
99 | ],
100 | data: {
101 | i18n: {
102 | isRoot: true
103 | }
104 | }
105 | },
106 | {
107 | path: 'change-language/:languageCode',
108 | component: ChangeLanguageComponent
109 | },
110 |
111 | ...
112 |
113 | {
114 | path: '**',
115 | redirectTo: '',
116 | pathMatch: 'full'
117 | }
118 | ];
119 | ```
120 |
121 | #### I18N-ROOT
122 | The route configuration above shows that, one of the routes contains `i18n` property inside the `data` property.
123 |
124 | When its value is set to `true`, **`@ngx-i18n-router/core`** will **prepend** descendants of this route with the **2-letter
125 | language code** (*ex: `en`/`fr`/`de`/`nl`/`tr`*).
126 |
127 | > We call this route, with `data` property containing `i18n\isRoot` set to `true`, **`I18N-ROOT`** of **Angular** application.
128 |
129 | ```
130 | (English)
131 |
132 | http://mysite.com/about -> http://mysite.com/en/about
133 | ```
134 |
135 | **Note**: There must be a maximum of **one** `i18n\isRoot` in the route configuration, and (*if exists*) this must be placed
136 | inside the `data` property of any of the **first-level** routes.
137 |
138 | **Note**: It is always a good practice to have **exactly one route** (*and component*) in the `Home` feature module, with
139 | a path set to `''` (*see `home.routes.ts` in this readme*).
140 |
141 | #### Non-prefixed routes
142 | Routes outside **`I18N-ROOT`** scope will **NOT** have this **2-letter language code** prefix. It allows the **Angular**
143 | application to support both `prefixed` and `non-prefixed` routes.
144 |
145 | ```
146 | (English)
147 |
148 | http://mysite.com/about -> http://mysite.com/en/about
149 | http://mysite.com/sitemap
150 | http://mysite.com/admin
151 | ```
152 |
153 | #### Catchall route
154 | There must be a **catchall route** in the route configuration, redirecting to the **`I18N-ROOT`** of the app (*here, redirects
155 | to `''`*). The `redirectTo` property will be reset to the **2-letter language code** by **`@ngx-i18n-router/core`**.
156 |
157 | ### app.module configuration
158 | Import `I18NRouterModule` using the mapping `'@ngx-i18n-router/core'` and append `I18NRouterModule.forRoot(routes, {...})`
159 | within the imports property of **app.module** (*considering the app.module is the core module in Angular application*).
160 |
161 | Also, don't forget to provide `I18N_ROUTER_PROVIDERS` within the providers property of **app.module**.
162 |
163 | **Note**: `I18N_ROUTER_PROVIDERS` must be imported in the **app.module** (*instead of in `I18NRouterModule`*), to resolve
164 | the `I18NRouterService` dependency at the **uppermost level** (*otherwise child modules will have different instances of
165 | `I18NRouterService`*).
166 |
167 | #### app.module.ts
168 | ```TypeScript
169 | ...
170 | import { I18NRouterModule, I18N_ROUTER_PROVIDERS } from '@ngx-i18n-router/core';
171 | ...
172 |
173 | @NgModule({
174 | declarations: [
175 | AppComponent,
176 | ...
177 | ],
178 | ...
179 | imports: [
180 | RouterModule.forRoot(routes),
181 | I18NRouterModule.forRoot(routes),
182 | ...
183 | ],
184 | ...
185 | providers: [
186 | I18N_ROUTER_PROVIDERS,
187 | ...
188 | ],
189 | ...
190 | bootstrap: [AppComponent]
191 | })
192 | ```
193 |
194 | ### Feature modules configuration
195 | Import `I18NRouterModule` using the mapping `'@ngx-i18n-router/core'` and append `I18NRouterModule.forChild(routes, moduleKey)`
196 | within the imports property of the **feature module**. The `moduleKey` parameter for the `forChild` method obviously refers
197 | to the **module's root path** (*in kebab-case*).
198 |
199 | #### home.routes.ts
200 | ```TypeScript
201 | export const routes: Routes = [
202 | {
203 | path: '',
204 | component: HomeComponent
205 | }
206 | ];
207 | ```
208 |
209 | #### home.module.ts
210 | ```TypeScript
211 | ...
212 | import { I18NRouterModule } from '@ngx-i18n-router/core';
213 | ...
214 |
215 | @NgModule({
216 | ...
217 | imports: [
218 | //RouterModule.forChild(routes),
219 | I18NRouterModule.forChild(routes, 'home'),
220 | ...
221 | ],
222 | ...
223 | })
224 | ```
225 |
226 | #### about.routes.ts
227 | ```TypeScript
228 | export const routes: Routes = [
229 | {
230 | path: '',
231 | component: AboutComponent
232 | },
233 | {
234 | path: 'us/:topicId',
235 | component: AboutUsComponent
236 | },
237 | {
238 | path: 'banana',
239 | component: AboutBananaComponent
240 | },
241 | {
242 | path: 'apple/:fruitId/pear',
243 | component: AboutApplePearComponent
244 | }
245 | ];
246 | ```
247 |
248 | #### about.module.ts
249 | ```TypeScript
250 | ...
251 | import { I18NRouterModule } from '@ngx-i18n-router/core';
252 | ...
253 |
254 | @NgModule({
255 | ...
256 | imports: [
257 | //RouterModule.forChild(routes),
258 | I18NRouterModule.forChild(routes, 'about'),
259 | ...
260 | ],
261 | ...
262 | })
263 | ```
264 |
265 | **Note**: You must comment (*or better delete*) the line with `RouterModule.forChild(routes)`, in order to get **`@ngx-i18n-router/core`**
266 | working. `forChild` method of `I18NRouterModule` provides routes for **feature modules** itself (*if imports both `RouterModule`
267 | and `I18NRouterModule`, it will cause **`@ngx-i18n-router/core`** to malfunction, even crash*).
268 |
269 | ### app.component configuration
270 | Import `I18NRouterService` using the mapping `'@ngx-i18n-router/core'` and **inject** it in the constructor of **app.component**
271 | (*considering the app.component is the bootstrap component in Angular application*).
272 |
273 | Then, invoke the `init` method to fetch **route translations** loaded during application initialization and allow the use
274 | of **`@ngx-i18n-router/core`** by the **Angular** app.
275 |
276 | Lastly, you need to invoke the `changeLanguage` method by supplying the **2-letter language code**, which translates routes
277 | to the specified language.
278 |
279 | #### app.component.ts
280 | ```TypeScript
281 | ...
282 | import { I18NRouterService } from '@ngx-i18n-router/core';
283 | ...
284 |
285 | @Component({
286 | ...
287 | })
288 | export class AppComponent implements OnInit {
289 | ...
290 | constructor(private readonly i18nRouter: I18NRouterService) {
291 | // invoking the `init` method with false won't allow the use of i18n-router,
292 | // would be handy in the case you need to use i18n-router programmatically
293 | i18nRouter.init();
294 | }
295 | ...
296 | ngOnInit(): void {
297 | this.i18nRouter.changeLanguage('en');
298 | }
299 | ...
300 | }
301 | ```
302 |
303 | ## Settings
304 | You can call the [forRoot] static method using `I18NRouterStaticLoader`. By default, it is configured to pass the **routes**
305 | to **`@ngx-i18n-router/core`** and have no translations.
306 |
307 | > You can customize this behavior (*and ofc other settings*) by supplying **route translations** to `I18NRouterStaticLoader`.
308 |
309 | If you provide route translations using a `JSON` file or an `API`, you can call the [forRoot] static method using the `I18NRouterHttpLoader`.
310 | By default, it is configured to retrieve **route translations** from the path `/routes.json` (*if not specified*).
311 |
312 | > You can customize this behavior (and ofc other settings) by supplying a **file path/api endpoint** to I18NRouterHttpLoader.
313 |
314 | You can also use the [@ngx-i18n-router/config-loader], to **reduce** the **amount** of `HTTP` requests during application
315 | initialization, by including **route translations** within the **application settings** - if [@ngx-config/core] is already
316 | used to retrieve settings by the **Angular** app.
317 |
318 | The following examples show the use of an exported function (*instead of an inline function*) for [AoT compilation].
319 |
320 | ### Setting up `I18NRouterModule` to use `I18NRouterStaticLoader`
321 | #### app.module.ts
322 | ```TypeScript
323 | ...
324 | import { I18NRouterModule, I18NRouterLoader, I18NRouterStaticLoader, I18N_ROUTER_PROVIDERS, RAW_ROUTES } from '@ngx-i18n-router/core';
325 | ...
326 |
327 | export function i18nRouterFactory(rawRoutes: Routes): I18NRouterLoader {
328 | return new I18NRouterStaticLoader({
329 | routes: rawRoutes,
330 | translations: {
331 | "en": {
332 | "ROOT.ABOUT": "about",
333 | "ROOT.ABOUT.US": "us",
334 | "ROOT.ABOUT.BANANA": "banana",
335 | "ROOT.ABOUT.APPLE": "apple",
336 | "ROOT.ABOUT.APPLE.PEAR": "pear",
337 | "CHANGE_LANGUAGE": "change-language"
338 | },
339 | "tr": {
340 | "ROOT.ABOUT": "hakkinda",
341 | "ROOT.ABOUT.US": "biz",
342 | "ROOT.ABOUT.BANANA": "muz",
343 | "ROOT.ABOUT.APPLE": "elma",
344 | "ROOT.ABOUT.APPLE.PEAR": "armut",
345 | "CHANGE_LANGUAGE": "dil-secimi"
346 | }
347 | }
348 | });
349 | }
350 |
351 | ...
352 |
353 | @NgModule({
354 | declarations: [
355 | AppComponent,
356 | ...
357 | ],
358 | ...
359 | imports: [
360 | RouterModule.forRoot(routes),
361 | I18NRouterModule.forRoot(routes, [
362 | {
363 | provide: I18NRouterLoader,
364 | useFactory: (i18nRouterFactory),
365 | deps: [RAW_ROUTES]
366 | }
367 | ]),
368 | ...
369 | ],
370 | ...
371 | providers: [
372 | I18N_ROUTER_PROVIDERS,
373 | ...
374 | ],
375 | ...
376 | bootstrap: [AppComponent]
377 | })
378 | ```
379 |
380 | `I18NRouterStaticLoader` has one parameter:
381 | - **providedSettings**: `I18NRouterSettings` : i18n-router settings
382 | - **routes**: `Routes`: raw routes
383 | - **translations**: `any` : route translations
384 |
385 | ### Setting up `I18NRouterModule` to use `I18NRouterHttpLoader`
386 | If you provide route translations using a `JSON` file or an `API`, you can call the [forRoot] static method using the `I18NRouterHttpLoader`.
387 | By default, it is configured to retrieve **route translations** from the endpoint `/routes.json` (*if not specified*).
388 |
389 | > You can customize this behavior (*and ofc other settings*) by supplying a **api endpoint** to `I18NRouterHttpLoader`.
390 |
391 | You can find detailed information about the usage guidelines for the `ConfigHttpLoader` [here](https://github.com/fulls1z3/ngx-i18n-router/tree/master/packages/@ngx-i18n-router/http-loader).
392 |
393 | ### Setting up `I18NRouterModule` to use `I18NRouterConfigLoader`
394 | `I18NRouterConfigLoader` provides route translations to **`@ngx-i18n-router/core`** using **`@ngx-config/core`**.
395 |
396 | You can find detailed information about the usage guidelines for the `I18NRouterConfigLoader` [here](https://github.com/fulls1z3/ngx-i18n-router/tree/master/packages/@ngx-i18n-router/config-loader).
397 |
398 | ### Translations object
399 | The translations object is designed to contain **route translations** in every **language** supported by **Angular** application,
400 | in **JSON format**.
401 |
402 | When the `changeLanguage` method of **`@ngx-i18n-router/core`** is invoked, **route configuration** is **reset** based on
403 | the supplied translations.
404 |
405 | You should use the following **data structure** while working with the translation object:
406 | - Assume that there're a number of **{N}** supported languages. The object contains **{N}** times first-level children,
407 | **key**ed with its **2-letter language code** and **value**d by translations specific to that language.
408 | - Language keys are in **lowercase**.
409 |
410 | ```
411 | ex: Angular app supports en, fr and tr
412 |
413 | {
414 | "en": { ... },
415 | "fr": { ... },
416 | "tr": { ... }
417 | }
418 | ```
419 |
420 | - Routes within the **`I18N-ROOT`** scope must be **key**ed with the `ROOT` **prefix**, followed by a dot char `.`, and
421 | then the **moduleKey** (*module's root path in kebab-case*).
422 | - Route **key**s are followed (*if any, after prefixes*) by the `path` attribute of routes.
423 | - Route **key**s are in **UPPERCASE**.
424 | - Dot char `.` is used as a **separator** between **prefix**es/**part**s (*ex: ROOT.ABOUT*).
425 |
426 | ```
427 | ex: Angular app supports en and tr
428 |
429 | modules:
430 | AboutModule (path: 'about')
431 |
432 | components:
433 | AboutComponent (path: '')
434 |
435 | routes:
436 | http://mysite.com/about -> http://mysite.com/en/about, http://mysite.com/tr/hakkinda
437 |
438 | {
439 | "en": {
440 | "ROOT.ABOUT": "about"
441 | },
442 | "tr": {
443 | "ROOT.ABOUT": "iletisim"
444 | }
445 | }
446 | ```
447 |
448 | - Routes outside the **`I18N-ROOT`** scope must be **key**ed with the **moduleKey** (*module's root path in kebab-case*).
449 |
450 | ```
451 | ex: Angular app supports en and tr
452 |
453 | modules:
454 | AboutModule (path: 'about')
455 | SitemapModule (path: 'sitemap')
456 |
457 | components:
458 | AboutComponent (path: '')
459 | SitemapComponent (path: '')
460 |
461 | routes:
462 | http://mysite.com/about -> http://mysite.com/en/about, http://mysite.com/tr/hakkinda
463 | http://mysite.com/sitemap -> http://mysite.com/sitemap
464 |
465 | {
466 | "en": {
467 | "ROOT.ABOUT": "about"
468 | },
469 | "tr": {
470 | "ROOT.ABOUT": "iletisim"
471 | }
472 | }
473 | ```
474 |
475 | - **Underscores** `_` must be used instead of **dashes** `-` in the route **key**s (*using dashes in keys causes errors*).
476 | They're automatically replaced with **dashes** `-` while `I18nRouterService` is **translating the routes**.
477 |
478 | ```
479 | ex: Angular app supports en and tr
480 |
481 | modules:
482 | AboutModule (path: 'about')
483 | SitemapModule (path: 'site-map')
484 |
485 | components:
486 | AboutComponent (path: '')
487 | SitemapComponent (path: '')
488 |
489 | routes:
490 | http://mysite.com/about -> http://mysite.com/en/about, http://mysite.com/tr/hakkinda
491 | http://mysite.com/site-map -> http://mysite.com/site-map, http://mysite.com/site-haritasi
492 |
493 | {
494 | "en": {
495 | "ROOT.ABOUT": "about",
496 | "SITE_MAP": "site-map"
497 | },
498 | "tr": {
499 | "ROOT.ABOUT": "iletisim",
500 | "SITE_MAP": "site-haritasi"
501 | }
502 | }
503 | ```
504 |
505 | - Route **path**s are split by **slashes** `/` into string **part**s. When **key**ing and they're separated by **dots**
506 | `.` (*ex: path: 'goes/to/your-page' -> 'GOES.TO.YOUR_PAGE'*).
507 | - Route **params** (*followed by colon*) are **not needed to be included** in the translations object (*ex: path: goes/to/:id/page
508 | -> 'GOES.TO.PAGE'*)
509 |
510 | ```
511 | ex: Angular app supports en and tr
512 |
513 | modules:
514 | AboutModule (path: 'about'), ContactModule (path: 'contact'), ProfileComponent (path: 'profile')
515 | SitemapModule (path: 'site-map')
516 |
517 | components:
518 | AboutComponent (path: ''), ContactComponent (path: ''), ContactMapComponent (path: 'map'), ProfileComponent (path: 'profile'), ProfileEditComponent (path: 'profile/:id/edit')
519 | SitemapComponent (path: '')
520 |
521 | routes:
522 | http://mysite.com/about -> http://mysite.com/en/about, http://mysite.com/tr/hakkinda
523 | http://mysite.com/contact -> http://mysite.com/en/contact, http://mysite.com/tr/iletisim
524 | http://mysite.com/contact/map -> http://mysite.com/en/contact/map, http://mysite.com/tr/iletisim/harita
525 | http://mysite.com/profile -> http://mysite.com/en/profile, http://mysite.com/tr/profil
526 | http://mysite.com/profile/:id/edit -> http://mysite.com/en/profile/:id/edit, http://mysite.com/tr/profil/:id/duzenle
527 |
528 | {
529 | "en": {
530 | "ROOT.ABOUT": "about",
531 | "ROOT.CONTACT": "contact",
532 | "ROOT.PROFILE": "profile",
533 | "ROOT.PROFILE.EDIT": "edit",
534 | "SITE_MAP": "site-map"
535 | },
536 | "tr": {
537 | "ROOT.ABOUT": "iletisim",
538 | "ROOT.CONTACT": "hakkimizda",
539 | "ROOT.PROFILE": "profil",
540 | "ROOT.PROFILE.EDIT": "duzenle",
541 | "SITE_MAP": "site-haritasi"
542 | }
543 | }
544 | ```
545 |
546 | > :+1: Hooyah! It was quite a long story, but **`@ngx-i18n-router/core`** will now translate each `path` and `redirectTo`
547 | property of routes.
548 |
549 | ## Change language (runtime)
550 | Import `I18NRouterService` using the mapping `'@ngx-i18n-router/core'` and **inject** it in the constructor of **change-language.component**
551 | (*considering the change-language.component changes the language in Angular application*).
552 |
553 | Then, invoke the `changeLanguage` method by supplying the **2-letter language code**, which translates routes to the destination
554 | language.
555 |
556 | ```TypeScript
557 | ...
558 | import { I18NRouterService } from '@ngx-i18n-router/core';
559 | ...
560 |
561 | @Component({
562 | ...
563 | })
564 | export class ChangeLanguageComponent implements OnInit {
565 | ...
566 | constructor(private readonly route: ActivatedRoute,
567 | private readonly i18nRouter: I18NRouterService,
568 | private readonly router: Router) { }
569 | ...
570 | ngOnInit(): void {
571 | this.route.params.subscribe(params => {
572 | var languageCode = params['languageCode'];
573 |
574 | if (languageCode)
575 | // change language
576 | this.i18nRouter.changeLanguage(languageCode);
577 |
578 | this.router.navigate(['/']);
579 | });
580 | }
581 | ...
582 | }
583 | ```
584 |
585 | ## Pipe
586 | `I18nRouterPipe` is used to **prefix** and **translate** `routerLink` directive's content. Pipe can be appended **ONLY**
587 | to a single **empty string** in the `routerLink`'s definition or to an **entire array** element:
588 |
589 | ```Html
590 | Home
591 | About
592 | About us
593 | Banana Republic
594 | (apple || pear)
595 | ```
596 |
597 | Example for Turkish language and link to 'about':
598 |
599 | ```
600 | ['about'] | i18nRouter -> '/tr/hakkinda'
601 | ```
602 |
603 | ## Workaround for '@ngtools/webpack'
604 | **`@ngx-i18n-router/core`** does not work with [angular-cli] (*yet*), and giving the following error during [AoT compilation]:
605 |
606 | > `ERROR in Cannot read property 'loadChildren' of undefined`
607 |
608 | **`@ngx-i18n-router/core`** injects routes with the `ROUTES` DI token using the `useFactory` property. However [@ngtools/webpack]
609 | forces routes to be *static*, and prevents code splitting (*for lazy-loaded modules*) by third parties.
610 |
611 | This issue is caused by the `ngtools_impl` located in the package `@angular/compiler-cli`.
612 |
613 | You can track the actual status of this issue at the following URLs:
614 | - https://github.com/fulls1z3/ngx-i18n-router/issues/2
615 | - https://github.com/angular/angular/issues/15305
616 |
617 | On the other hand, the [ng-router-loader] (*together with [awesome-typescipt-loader]*) is safe to go with - it compiles
618 | without a problem. There's an overhead: you need to **manually** configure **build tools** (*dev/prod sever, task runners,
619 | [webpack], etc*).
620 |
621 | If you really need to stick to [angular-cli], you can use the following workaround, by changing the contents of `/node_modules/@angular/compiler-cli/src/ngtools_impl.js`
622 | as described below:
623 |
624 | - **Method name:** `_collectRoutes`
625 | - **Line number:** 139
626 | - **Replacement:** comment the line containing `return routeList.concat(p.useValue);`, and replace with:
627 | ```JavaScript
628 | if (p.useFactory != null) {
629 | return routeList.concat(p.useFactory);
630 | } else {
631 | return routeList.concat(p.useValue);
632 | }
633 | ```
634 |
635 | #### [ngtools_impl.js](https://gist.github.com/fulls1z3/ca7541eeccc5b195f4854ff39d322d0e#file-ngtools_impl-js-L138)
636 | ```JavaScript
637 | function _collectRoutes(providers, reflector, ROUTES) {
638 | return providers.reduce(function (routeList, p) {
639 | if (p.provide === ROUTES) {
640 | // return routeList.concat(p.useValue);
641 | if (p.useFactory != null) {
642 | return routeList.concat(p.useFactory);
643 | } else {
644 | return routeList.concat(p.useValue);
645 | }
646 | }
647 | else if (Array.isArray(p)) {
648 | return routeList.concat(_collectRoutes(p, reflector, ROUTES));
649 | }
650 | else {
651 | return routeList;
652 | }
653 | }, []);
654 | }
655 | ```
656 |
657 | ## Credits
658 | - [localize-router](https://github.com/Greentube/localize-router): An implementation of routes localization for Angular 2
659 |
660 | ## License
661 | The MIT License (MIT)
662 |
663 | Copyright (c) 2018 [Burak Tasci]
664 |
665 | [master]: https://github.com/ngx-i18n-router/core/tree/master
666 | [5.x.x]: https://github.com/ngx-i18n-router/core/tree/5.x.x
667 | [ng-seed/universal]: https://github.com/ng-seed/universal
668 | [fulls1z3/example-app]: https://github.com/fulls1z3/example-app
669 | [@ngx-i18n-router/config-loader]: https://github.com/fulls1z3/ngx-i18n-router/tree/master/packages/@ngx-i18n-router/config-loader
670 | [@ngx-config/core]: https://github.com/fulls1z3/ngx-config/tree/master/packages/@ngx-config/core
671 | [@ngx-cache/core]: https://github.com/fulls1z3/ngx-cache/tree/master/packages/@ngx-cache/core
672 | [forRoot]: https://angular.io/docs/ts/latest/guide/ngmodule.html#!#core-for-root
673 | [angular-cli]: https://github.com/angular/angular-cli
674 | [AoT compilation]: https://angular.io/docs/ts/latest/cookbook/aot-compiler.html
675 | [@ngtools/webpack]: https://www.npmjs.com/package/@ngtools/webpack
676 | [ng-router-loader]: https://github.com/shlomiassaf/ng-router-loader
677 | [awesome-typescipt-loader]: https://github.com/s-panferov/awesome-typescript-loader
678 | [webpack]: http://webpack.github.io
679 | [Burak Tasci]: https://github.com/fulls1z3
680 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/core/index.ts:
--------------------------------------------------------------------------------
1 | // angular
2 | import {
3 | ANALYZE_FOR_ENTRY_COMPONENTS, APP_INITIALIZER, Inject, InjectionToken, ModuleWithProviders, NgModule, Optional,
4 | SkipSelf
5 | } from '@angular/core';
6 | import { provideRoutes, Router, RouterModule, Routes, ROUTES } from '@angular/router';
7 |
8 | // module
9 | import { I18NRouterLoader, I18NRouterStaticLoader } from './src/i18n-router.loader';
10 | import { I18NRouterPipe } from './src/i18n-router.pipe';
11 | import { I18NRouterService, provideChildRoutes } from './src/i18n-router.service';
12 |
13 | export * from './src/models/i18n-router-settings';
14 | export * from './src/i18n-router.loader';
15 | export * from './src/i18n-router.pipe';
16 | export * from './src/i18n-router.service';
17 |
18 | export const RAW_ROUTES = new InjectionToken('RAW_ROUTES');
19 | export const I18N_ROUTER_PROVIDERS: Array = [
20 | I18NRouterService
21 | ];
22 |
23 | // for AoT compilation
24 | export function i18nRouterFactory(routes: Routes): I18NRouterLoader {
25 | return new I18NRouterStaticLoader({routes});
26 | }
27 |
28 | export function initializerFactory(loader: I18NRouterLoader): any {
29 | // workaround for AoT compilation
30 | const res = () => loader.loadTranslations();
31 |
32 | return res;
33 | }
34 |
35 | export const I18N_ROUTER_FORROOT_GUARD = new InjectionToken('I18N_ROUTER_FORROOT_GUARD');
36 | export const MODULE_KEY = new InjectionToken('MODULE_KEY');
37 |
38 | @NgModule({
39 | imports: [RouterModule],
40 | declarations: [I18NRouterPipe],
41 | exports: [
42 | I18NRouterPipe,
43 | RouterModule
44 | ]
45 | })
46 | export class I18NRouterModule {
47 | static forRoot(routes: Routes,
48 | configuredProviders: Array = [{
49 | provide: I18NRouterLoader,
50 | useFactory: (i18nRouterFactory),
51 | deps: [RAW_ROUTES]
52 | }]): ModuleWithProviders {
53 | return {
54 | ngModule: I18NRouterModule,
55 | providers: [
56 | ...configuredProviders,
57 | {
58 | provide: APP_INITIALIZER,
59 | useFactory: (initializerFactory),
60 | deps: [I18NRouterLoader],
61 | multi: true
62 | },
63 | {
64 | provide: RAW_ROUTES,
65 | useValue: routes
66 | },
67 | {
68 | provide: I18N_ROUTER_FORROOT_GUARD,
69 | useFactory: (provideForRootGuard),
70 | deps: [[Router, new Optional(), new SkipSelf()]]
71 | }
72 | ]
73 | };
74 | }
75 |
76 | static forChild(routes: Routes, moduleKey: string): ModuleWithProviders {
77 | return {
78 | ngModule: I18NRouterModule,
79 | providers: [
80 | provideRoutes([]),
81 | {
82 | provide: RAW_ROUTES,
83 | useValue: routes
84 | },
85 | {
86 | provide: MODULE_KEY,
87 | useValue: moduleKey
88 | },
89 | {
90 | provide: ROUTES,
91 | useFactory: (provideChildRoutes),
92 | deps: [I18NRouterService, RAW_ROUTES, MODULE_KEY],
93 | multi: true
94 | },
95 | {
96 | provide: ANALYZE_FOR_ENTRY_COMPONENTS,
97 | useValue: routes,
98 | multi: true
99 | }
100 | ]
101 | };
102 | }
103 |
104 | constructor(@Optional() @Inject(I18N_ROUTER_FORROOT_GUARD) guard: any) {
105 | // NOTE: inject token
106 | }
107 | }
108 |
109 | export function provideForRootGuard(router: Router): any {
110 | if (router)
111 | throw new Error(
112 | 'I18NRouterModule.forRoot() called twice. Child modules should use I18NRouterModule.forChild() instead.');
113 |
114 | return 'guarded';
115 | }
116 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ngx-i18n-router/core",
3 | "version": "5.0.0",
4 | "description": "Route internationalization utility for Angular",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/fulls1z3/ngx-i18n-router.git"
8 | },
9 | "keywords": [
10 | "lazy loading",
11 | "localization",
12 | "i18n",
13 | "async",
14 | "localized router",
15 | "router",
16 | "i18n router",
17 | "angular"
18 | ],
19 | "author": {
20 | "name": "Burak Tasci",
21 | "email": "me@fulls1z3.com"
22 | },
23 | "license": "MIT",
24 | "bugs": {
25 | "url": "https://github.com/fulls1z3/ngx-i18n-router/issues"
26 | },
27 | "homepage": "https://github.com/fulls1z3/ngx-i18n-router/tree/master/packages/@ngx-i18n-router/core#readme",
28 | "main": "./bundles/core.umd.js",
29 | "module": "./@ngx-i18n-router/core.es5.js",
30 | "es2015": "./@ngx-i18n-router/core.js",
31 | "typings": "./core.d.ts",
32 | "dependencies": {
33 | "tslib": "~1.8.1"
34 | },
35 | "peerDependencies": {
36 | "@angular/core": ">=5.0.0 <6.0.0",
37 | "@angular/platform-browser-dynamic": ">=5.0.0 <6.0.0",
38 | "@angular/router": ">=5.0.0 <6.0.0",
39 | "lodash": ">=4.17.4"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/core/src/i18n-router.loader.ts:
--------------------------------------------------------------------------------
1 | // angular
2 | import { Routes } from '@angular/router';
3 |
4 | // libs
5 | import * as _ from 'lodash';
6 |
7 | // module
8 | import { I18NRouterSettings } from './models/i18n-router-settings';
9 |
10 | export abstract class I18NRouterLoader {
11 | abstract get routes(): Routes;
12 |
13 | abstract get translations(): any;
14 |
15 | abstract loadTranslations(): any;
16 | }
17 |
18 | export class I18NRouterStaticLoader implements I18NRouterLoader {
19 | get routes(): Routes {
20 | return _.map(this.providedSettings.routes, _.cloneDeep);
21 | }
22 |
23 | get translations(): any {
24 | return this.providedSettings.translations;
25 | }
26 |
27 | constructor(private readonly providedSettings: I18NRouterSettings = {}) {
28 | }
29 |
30 | loadTranslations(): any {
31 | return Promise.resolve(this.translations);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/core/src/i18n-router.pipe.ts:
--------------------------------------------------------------------------------
1 | // angular
2 | import { Injectable, Pipe, PipeTransform } from '@angular/core';
3 |
4 | // libs
5 | import * as _ from 'lodash';
6 |
7 | // module
8 | import { I18NRouterService, ROOT_ROUTE_PREFIX } from './i18n-router.service';
9 |
10 | @Injectable()
11 | @Pipe({
12 | name: 'i18nRouter',
13 | pure: false
14 | })
15 | export class I18NRouterPipe implements PipeTransform {
16 | constructor(private readonly i18nRouter: I18NRouterService) {
17 | }
18 |
19 | transform(value: string | Array): string {
20 | if (typeof value === 'string' && _.get(value, 'length', 0))
21 | throw new Error('Query must be an empty string or an array!');
22 |
23 | if (!this.i18nRouter.languageCode || !this.i18nRouter.useLocalizedRoutes)
24 | return `/${typeof value === 'string' ? value : value.join('/')}`;
25 |
26 | if (_.get(value, 'length', 0) === 0)
27 | return `/${this.i18nRouter.languageCode}`;
28 |
29 | return `/${this.translateQuery(value)}`;
30 | }
31 |
32 | private translateQuery(value: string | Array): string {
33 | const translateBatch: Array = [];
34 | let batchKey = '';
35 |
36 | (value as Array).forEach((segment: any, index: number) => {
37 | if (typeof segment === 'string') {
38 | let prefix = '';
39 |
40 | let currentKey = `${ROOT_ROUTE_PREFIX}.${segment}`;
41 |
42 | if (index === 0) {
43 | prefix = this.i18nRouter.getTranslation(currentKey);
44 |
45 | if (prefix) {
46 | batchKey = currentKey;
47 | translateBatch.push(this.i18nRouter.languageCode);
48 | }
49 | }
50 |
51 | currentKey = index === 0 ? (prefix ? batchKey : segment) : `${batchKey}.${segment}`;
52 | const translatedSegment = this.i18nRouter.getTranslation(currentKey);
53 |
54 | if (translatedSegment)
55 | batchKey = currentKey;
56 |
57 | translateBatch.push(translatedSegment || segment);
58 | } else
59 | translateBatch.push(segment);
60 | });
61 |
62 | return translateBatch.join('/');
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/core/src/i18n-router.service.ts:
--------------------------------------------------------------------------------
1 | // angular
2 | import { Injectable } from '@angular/core';
3 | import { Route, Router, Routes } from '@angular/router';
4 |
5 | // libs
6 | import * as _ from 'lodash';
7 |
8 | // module
9 | import { I18NRouterLoader } from './i18n-router.loader';
10 |
11 | export const ROOT_ROUTE_PREFIX = 'ROOT';
12 |
13 | @Injectable()
14 | export class I18NRouterService {
15 | languageCode: string;
16 | useLocalizedRoutes: boolean;
17 |
18 | protected readonly routes: Routes;
19 | protected translations: any;
20 |
21 | constructor(readonly loader: I18NRouterLoader,
22 | private readonly router: Router) {
23 | this.routes = _.map(this.loader.routes, _.cloneDeep);
24 | }
25 |
26 | init(useLocalizedRoutes = true): void {
27 | if (!useLocalizedRoutes)
28 | return;
29 |
30 | this.useLocalizedRoutes = true;
31 | this.translations = this.loader.translations;
32 | }
33 |
34 | changeLanguage(languageCode: string): void {
35 | if (!this.useLocalizedRoutes)
36 | return;
37 |
38 | if (!this.translations[languageCode])
39 | return;
40 |
41 | this.languageCode = languageCode;
42 |
43 | const rawRoutes = this.loader.routes;
44 | const i18nRoot = _.find(rawRoutes, route => _.get(route, 'data.i18n.isRoot', undefined));
45 |
46 | let routes: Routes = [];
47 |
48 | if (i18nRoot) {
49 | const rootPath = this.translateRoute(i18nRoot, 'path').path || i18nRoot.path;
50 | routes = [{
51 | path: '',
52 | redirectTo: this.interpolateRoute(rootPath),
53 | pathMatch: 'full'
54 | }];
55 | }
56 |
57 | const translatedRoutes = this.translateRoutes(rawRoutes);
58 | routes = routes.concat(translatedRoutes);
59 |
60 | this.router.resetConfig(routes);
61 | }
62 |
63 | getTranslation(key: string): string {
64 | key = key.replace(/-/, '_');
65 |
66 | if (!this.translations[this.languageCode][key.toUpperCase()])
67 | return undefined;
68 |
69 | return this.translations[this.languageCode][key.toUpperCase()];
70 | }
71 |
72 | translateRoutes(routes: Routes, moduleKey = ''): Routes {
73 | const translatedRoutes: Array = [];
74 |
75 | routes.forEach((route: Route) => {
76 | if (_.isArray(route.children)) {
77 | if (_.get(route, 'data.i18n.isRoot', false))
78 | route.path = this.interpolateRoute(route.path);
79 | else if (route.path)
80 | route = this.translateRoute(route, 'path', moduleKey);
81 |
82 | route.children = this.translateRoutes(route.children, moduleKey);
83 | } else if (!moduleKey && route.path === '**')
84 | route.redirectTo = this.interpolateRoute(route.redirectTo);
85 | else {
86 | if (route.path)
87 | route = this.translateRoute(route, 'path', moduleKey);
88 |
89 | if (route.redirectTo)
90 | route = this.translateRoute(route, 'redirectTo', moduleKey);
91 | }
92 |
93 | translatedRoutes.push(route);
94 | });
95 |
96 | return translatedRoutes;
97 | }
98 |
99 | private interpolateRoute(path: string): string {
100 | if (!path || path.length === 0)
101 | return this.languageCode;
102 |
103 | path = _.filter(path.split('/'), (segment: string) => !!segment).join('/');
104 |
105 | return `${this.languageCode}/${path}`;
106 | }
107 |
108 | private translateRoute(route: Route, property: string, moduleKey = ''): Route {
109 | const translateBatch: Array = [];
110 | let batchKey = '';
111 |
112 | const key = _.filter(route[property].split('/'), segment => segment);
113 | const isRedirection = property === 'redirectTo' && _.startsWith(route[property], '/');
114 |
115 | (key as Array).forEach((segment: any, index: number) => {
116 | let prefix = '';
117 |
118 | let currentKey = `${ROOT_ROUTE_PREFIX}.${moduleKey && !isRedirection ? `${moduleKey}.` : ''}${segment}`;
119 |
120 | if (index === 0) {
121 | prefix = this.getTranslation(currentKey);
122 |
123 | if (prefix) {
124 | if (isRedirection)
125 | translateBatch.push(this.languageCode);
126 |
127 | batchKey = currentKey;
128 | }
129 | }
130 |
131 | currentKey = index === 0 ? (prefix ? batchKey : segment) : `${batchKey}.${segment}`;
132 | const translatedSegment = !_.startsWith(segment, ':') ? this.getTranslation(currentKey) : '';
133 |
134 | if (translatedSegment)
135 | batchKey = currentKey;
136 |
137 | translateBatch.push(translatedSegment || segment);
138 | });
139 |
140 | route[property] = translateBatch.join('/');
141 |
142 | if (isRedirection)
143 | route[property] = `/${route[property]}`;
144 |
145 | return route;
146 | }
147 | }
148 |
149 | export function provideChildRoutes(i18nRouter: I18NRouterService, routes: Routes, moduleKey: string): Routes {
150 | if (!i18nRouter.useLocalizedRoutes)
151 | return routes;
152 |
153 | return i18nRouter.translateRoutes(routes, moduleKey);
154 | }
155 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/core/src/models/i18n-router-settings.ts:
--------------------------------------------------------------------------------
1 | // angular
2 | import { Routes } from '@angular/router';
3 |
4 | export interface I18NRouterSettings {
5 | routes?: Routes;
6 | translations?: any;
7 | }
8 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/core/tests/common.ts:
--------------------------------------------------------------------------------
1 | // angular
2 | import { Component, NgModule } from '@angular/core';
3 | import { TestBed } from '@angular/core/testing';
4 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
5 | import { Route, Routes } from '@angular/router';
6 | import { RouterTestingModule } from '@angular/router/testing';
7 |
8 | // module
9 | import { I18N_ROUTER_PROVIDERS, I18NRouterModule } from '../index';
10 |
11 | @Component({template: ' '})
12 | // tslint:disable-next-line
13 | export class TestBootstrapComponent {
14 | }
15 |
16 | @Component({template: ''})
17 | // tslint:disable-next-line
18 | export class TestComponent {
19 | }
20 |
21 | @NgModule({
22 | imports: [RouterTestingModule],
23 | declarations: [
24 | TestBootstrapComponent,
25 | TestComponent
26 | ]
27 | })
28 | // tslint:disable-next-line
29 | class TestSharedModule {
30 | }
31 |
32 | @NgModule({
33 | imports: [
34 | TestSharedModule,
35 | I18NRouterModule.forChild([
36 | {
37 | path: '',
38 | component: TestComponent
39 | }
40 | ],
41 | 'home')
42 | ]
43 | })
44 | // tslint:disable-next-line
45 | class TestHomeModule {
46 | }
47 |
48 | @NgModule({
49 | imports: [
50 | TestSharedModule,
51 | I18NRouterModule.forChild([
52 | {
53 | path: '',
54 | component: TestBootstrapComponent,
55 | children: [
56 | {
57 | path: 'us/:topicId',
58 | component: TestComponent
59 | },
60 | {
61 | path: 'banana',
62 | component: TestComponent
63 | },
64 | {
65 | path: 'apple/:fruitId/pear',
66 | component: TestComponent
67 | },
68 | {
69 | path: 'plum',
70 | redirectTo: '/about/banana',
71 | pathMatch: 'full'
72 | }
73 | ]
74 | }
75 | ],
76 | 'about')
77 | ]
78 | })
79 | // tslint:disable-next-line
80 | class TestAboutModule {
81 | }
82 |
83 | export const testRoutes: Routes = [
84 | {
85 | path: '',
86 | children: [
87 | {
88 | path: '',
89 | loadChildren: () => TestHomeModule
90 | },
91 | {
92 | path: 'about',
93 | loadChildren: () => TestAboutModule
94 | }
95 | ],
96 | data: {
97 | i18n: {
98 | isRoot: true
99 | }
100 | }
101 | },
102 | {
103 | path: 'change-language/:languageCode',
104 | component: TestComponent
105 | },
106 | {
107 | path: '**',
108 | redirectTo: '',
109 | pathMatch: 'full'
110 | }
111 | ];
112 |
113 | export const testTranslations = {
114 | en: {
115 | 'ROOT.ABOUT': 'about',
116 | 'ROOT.ABOUT.US': 'us',
117 | 'ROOT.ABOUT.BANANA': 'banana',
118 | 'ROOT.ABOUT.APPLE': 'apple',
119 | 'ROOT.ABOUT.APPLE.PEAR': 'pear',
120 | 'ROOT.ABOUT.PLUM': 'plum',
121 | CHANGE_LANGUAGE: 'change-language'
122 | },
123 | tr: {
124 | 'ROOT.ABOUT': 'hakkinda',
125 | 'ROOT.ABOUT.US': 'biz',
126 | // "ROOT.ABOUT.BANANA": 'muz',
127 | 'ROOT.ABOUT.APPLE': 'elma',
128 | 'ROOT.ABOUT.APPLE.PEAR': 'armut',
129 | 'ROOT.ABOUT.PLUM': 'erik',
130 | CHANGE_LANGUAGE: 'dil-secimi'
131 | }
132 | };
133 |
134 | export const testModuleConfig = (routes: Array = [], moduleOptions?: Array) => {
135 | TestBed.resetTestEnvironment();
136 |
137 | TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting())
138 | .configureTestingModule({
139 | imports: [
140 | TestSharedModule,
141 | RouterTestingModule.withRoutes(routes),
142 | I18NRouterModule.forRoot(routes, moduleOptions)
143 | ],
144 | providers: [
145 | I18N_ROUTER_PROVIDERS
146 | ]
147 | });
148 | };
149 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/core/tests/i18n-router.loader.spec.ts:
--------------------------------------------------------------------------------
1 | // angular
2 | import { TestBed } from '@angular/core/testing';
3 | import { Routes } from '@angular/router';
4 |
5 | // libs
6 | import * as _ from 'lodash';
7 |
8 | // module
9 | import { I18NRouterLoader, I18NRouterService, I18NRouterStaticLoader } from '../index';
10 | import { testModuleConfig, testRoutes, testTranslations } from './common';
11 |
12 | describe('@ngx-i18n-router/core:',
13 | () => {
14 | beforeEach(() => {
15 | const i18nRouterFactory = () => new I18NRouterStaticLoader({
16 | routes: testRoutes,
17 | translations: testTranslations
18 | });
19 |
20 | testModuleConfig(testRoutes, [{
21 | provide: I18NRouterLoader,
22 | useFactory: (i18nRouterFactory)
23 | }]);
24 | });
25 |
26 | describe('I18NRouterLoader',
27 | () => {
28 | it('should not return any routes & translations unless provided',
29 | () => {
30 | const loader = new I18NRouterStaticLoader();
31 | const loadedRoutes = loader.routes;
32 | const loadedTranslations = loader.translations;
33 |
34 | expect(loadedRoutes).toEqual([]);
35 | expect(loadedTranslations).toBeUndefined();
36 | });
37 |
38 | it('should be able to provide `I18NRouterStaticLoader`',
39 | () => {
40 | const i18nRouterFactory = () => new I18NRouterStaticLoader({
41 | routes: testRoutes,
42 | translations: testTranslations
43 | });
44 |
45 | testModuleConfig(testRoutes, [{
46 | provide: I18NRouterLoader,
47 | useFactory: (i18nRouterFactory)
48 | }]);
49 |
50 | const i18nRouter = TestBed.get(I18NRouterService);
51 |
52 | expect(I18NRouterStaticLoader).toBeDefined();
53 | expect(i18nRouter.loader).toBeDefined();
54 | expect(i18nRouter.loader instanceof I18NRouterStaticLoader).toBeTruthy();
55 | });
56 |
57 | it('should be able to provide any `I18NRouterLoader`',
58 | () => {
59 | class CustomLoader implements I18NRouterLoader {
60 | get routes(): Routes {
61 | return _.map(testRoutes, _.cloneDeep);
62 | }
63 |
64 | get translations(): any {
65 | return testTranslations;
66 | }
67 |
68 | loadTranslations(): any {
69 | return Promise.resolve({});
70 | }
71 | }
72 |
73 | testModuleConfig(testRoutes, [{
74 | provide: I18NRouterLoader,
75 | useClass: CustomLoader
76 | }]);
77 |
78 | const i18nRouter = TestBed.get(I18NRouterService);
79 |
80 | expect(CustomLoader).toBeDefined();
81 | expect(i18nRouter.loader).toBeDefined();
82 | expect(i18nRouter.loader instanceof CustomLoader).toBeTruthy();
83 | });
84 | });
85 | });
86 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/core/tests/i18n-router.pipe.spec.ts:
--------------------------------------------------------------------------------
1 | // angular
2 | import { inject } from '@angular/core/testing';
3 | import { Router } from '@angular/router';
4 |
5 | // module
6 | import { I18NRouterLoader, I18NRouterPipe, I18NRouterService, I18NRouterStaticLoader } from '../index';
7 | import { testModuleConfig, testRoutes, testTranslations } from './common';
8 |
9 | describe('@ngx-i18n-router/core:',
10 | () => {
11 | beforeEach(() => {
12 | const i18nRouterFactory = () => new I18NRouterStaticLoader({
13 | routes: testRoutes,
14 | translations: testTranslations
15 | });
16 |
17 | testModuleConfig(testRoutes, [{
18 | provide: I18NRouterLoader,
19 | useFactory: (i18nRouterFactory)
20 | }]);
21 | });
22 |
23 | describe('I18NRouterPipe',
24 | () => {
25 | it('is defined',
26 | inject([Router, I18NRouterService],
27 | (router: Router, i18nRouter: I18NRouterService) => {
28 | const pipe = new I18NRouterPipe(i18nRouter);
29 |
30 | expect(I18NRouterPipe).toBeDefined();
31 | expect(pipe).toBeDefined();
32 | expect(pipe instanceof I18NRouterPipe).toBeTruthy();
33 | }));
34 |
35 | it('should not translate routes w/o default initialization',
36 | inject([Router, I18NRouterService],
37 | (router: Router, i18nRouter: I18NRouterService) => {
38 | i18nRouter.init(false);
39 | i18nRouter.changeLanguage('tr');
40 |
41 | const pipe = new I18NRouterPipe(i18nRouter);
42 |
43 | let translatedPath = pipe.transform('');
44 | expect(translatedPath).toEqual('/');
45 |
46 | translatedPath = pipe.transform(['about', 'us']);
47 | expect(translatedPath).toEqual('/about/us');
48 | }));
49 |
50 | it('should not translate routes w/o translations',
51 | inject([Router, I18NRouterService],
52 | (router: Router, i18nRouter: I18NRouterService) => {
53 | i18nRouter.init();
54 | i18nRouter.changeLanguage('fr');
55 |
56 | const pipe = new I18NRouterPipe(i18nRouter);
57 |
58 | let translatedPath = pipe.transform('');
59 | expect(translatedPath).toEqual('/');
60 |
61 | translatedPath = pipe.transform(['about', 'us']);
62 | expect(translatedPath).toEqual('/about/us');
63 | }));
64 |
65 | it('should throw if you provide `non-empty` string query',
66 | inject([Router, I18NRouterService],
67 | (router: Router, i18nRouter: I18NRouterService) => {
68 | i18nRouter.init();
69 | i18nRouter.changeLanguage('en');
70 |
71 | const pipe = new I18NRouterPipe(i18nRouter);
72 |
73 | expect(() => pipe.transform('about'))
74 | .toThrowError('Query must be an empty string or an array!');
75 | }));
76 |
77 | it('should be able to partly translate routes w/missing translations',
78 | inject([Router, I18NRouterService],
79 | (router: Router, i18nRouter: I18NRouterService) => {
80 | i18nRouter.init();
81 | i18nRouter.changeLanguage('tr');
82 |
83 | const pipe = new I18NRouterPipe(i18nRouter);
84 |
85 | const translatedPath = pipe.transform(['about', 'banana']);
86 | expect(translatedPath).toEqual('/tr/hakkinda/banana');
87 | }));
88 |
89 | it('should be able to translate the `i18n-root` route',
90 | inject([Router, I18NRouterService],
91 | (router: Router, i18nRouter: I18NRouterService) => {
92 | i18nRouter.init();
93 | i18nRouter.changeLanguage('en');
94 |
95 | const pipe = new I18NRouterPipe(i18nRouter);
96 |
97 | const translatedPath = pipe.transform('');
98 | expect(translatedPath).toEqual('/en');
99 | }));
100 |
101 | it('should be able to translate a route inside the `i18n-root`',
102 | inject([Router, I18NRouterService],
103 | (router: Router, i18nRouter: I18NRouterService) => {
104 | i18nRouter.init();
105 | i18nRouter.changeLanguage('tr');
106 |
107 | const pipe = new I18NRouterPipe(i18nRouter);
108 |
109 | let translatedPath = pipe.transform(['about', 'us']);
110 | expect(translatedPath).toEqual('/tr/hakkinda/biz');
111 |
112 | translatedPath = pipe.transform(['about', 'apple', 1, 'pear']);
113 | expect(translatedPath).toEqual('/tr/hakkinda/elma/1/armut');
114 | }));
115 |
116 | it('should be able to translate a route outside the `i18n-root`',
117 | inject([Router, I18NRouterService],
118 | (router: Router, i18nRouter: I18NRouterService) => {
119 | i18nRouter.init();
120 | i18nRouter.changeLanguage('tr');
121 |
122 | const pipe = new I18NRouterPipe(i18nRouter);
123 |
124 | const translatedPath = pipe.transform(['change-language', 'en']);
125 | expect(translatedPath).toEqual('/dil-secimi/en');
126 | }));
127 | });
128 | });
129 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/core/tests/i18n-router.service.spec.ts:
--------------------------------------------------------------------------------
1 | // angular
2 | import { fakeAsync, inject, TestBed } from '@angular/core/testing';
3 | import { Router, Routes } from '@angular/router';
4 |
5 | // module
6 | import { I18NRouterLoader, I18NRouterService, I18NRouterStaticLoader } from '../index';
7 | import { TestBootstrapComponent, TestComponent, testModuleConfig, testRoutes, testTranslations } from './common';
8 |
9 | describe('@ngx-i18n-router/core:',
10 | () => {
11 | beforeEach(() => {
12 | const i18nRouterFactory = () => new I18NRouterStaticLoader({
13 | routes: testRoutes,
14 | translations: testTranslations
15 | });
16 |
17 | testModuleConfig(testRoutes, [{
18 | provide: I18NRouterLoader,
19 | useFactory: (i18nRouterFactory)
20 | }]);
21 | });
22 |
23 | describe('I18NRouterService',
24 | () => {
25 | it('is defined',
26 | inject([I18NRouterService],
27 | (i18nRouter: I18NRouterService) => {
28 | expect(I18NRouterService).toBeDefined();
29 | expect(i18nRouter).toBeDefined();
30 | expect(i18nRouter instanceof I18NRouterService).toBeTruthy();
31 | }));
32 |
33 | it('should not translate routes w/o default initialization',
34 | inject([I18NRouterService],
35 | (i18nRouter: I18NRouterService) => {
36 | const router = TestBed.get(Router);
37 |
38 | spyOn(router, 'resetConfig').and.callThrough();
39 |
40 | i18nRouter.init(false);
41 | i18nRouter.changeLanguage('en');
42 |
43 | expect(router.resetConfig).not.toHaveBeenCalled();
44 | expect(router.config).toEqual(testRoutes);
45 |
46 | const fixture = TestBed.createComponent(TestBootstrapComponent);
47 | fixture.detectChanges();
48 |
49 | router.navigate(['/'])
50 | .then(() => {
51 | expect(router.url).toEqual('/');
52 | });
53 | }));
54 |
55 | it('should not translate routes w/o translations',
56 | inject([Router, I18NRouterService],
57 | (router: Router, i18nRouter: I18NRouterService) => {
58 | spyOn(router, 'resetConfig').and.callThrough();
59 |
60 | i18nRouter.init();
61 | i18nRouter.changeLanguage('fr');
62 |
63 | expect(router.resetConfig).not.toHaveBeenCalled();
64 | expect(router.config).toEqual(testRoutes);
65 | }));
66 |
67 | it('should be able to `partly` translate routes w/missing translations',
68 | fakeAsync(inject([I18NRouterService],
69 | (i18nRouter: I18NRouterService) => {
70 | const router = TestBed.get(Router);
71 |
72 | i18nRouter.init();
73 | i18nRouter.changeLanguage('tr');
74 |
75 | const fixture = TestBed.createComponent(TestBootstrapComponent);
76 | fixture.detectChanges();
77 |
78 | router.navigate(['/tr/hakkinda/banana'])
79 | .then(() => {
80 | expect(router.url).toEqual('/tr/hakkinda/banana');
81 | });
82 | })));
83 |
84 | it('should be able to `catchall` redirect to `i18n-root`',
85 | fakeAsync(inject([I18NRouterService],
86 | (i18nRouter: I18NRouterService) => {
87 | const router = TestBed.get(Router);
88 |
89 | i18nRouter.init();
90 | i18nRouter.changeLanguage('en');
91 |
92 | const fixture = TestBed.createComponent(TestBootstrapComponent);
93 | fixture.detectChanges();
94 |
95 | router.navigate(['/about'])
96 | .then(() => {
97 | expect(router.url).toEqual('/en');
98 | });
99 | })));
100 |
101 | it('should be able to translate `path` property of routes',
102 | fakeAsync(inject([I18NRouterService],
103 | (i18nRouter: I18NRouterService) => {
104 | const router = TestBed.get(Router);
105 |
106 | i18nRouter.init();
107 | i18nRouter.changeLanguage('tr');
108 |
109 | const fixture = TestBed.createComponent(TestBootstrapComponent);
110 | fixture.detectChanges();
111 |
112 | router.navigate(['/tr/hakkinda'])
113 | .then(() => {
114 | expect(router.url).toEqual('/tr/hakkinda');
115 | });
116 | })));
117 |
118 | it('should be able to translate `redirectTo` property of routes',
119 | fakeAsync(inject([I18NRouterService],
120 | (i18nRouter: I18NRouterService) => {
121 | const router = TestBed.get(Router);
122 |
123 | i18nRouter.init();
124 | i18nRouter.changeLanguage('tr');
125 |
126 | const fixture = TestBed.createComponent(TestBootstrapComponent);
127 | fixture.detectChanges();
128 |
129 | router.navigate(['/tr/hakkinda/erik'])
130 | .then(() => {
131 | expect(router.url).toEqual('/tr/hakkinda/banana');
132 | });
133 | })));
134 |
135 | it('should be able to translate routes outside the `i18n-root`',
136 | fakeAsync(inject([I18NRouterService],
137 | (i18nRouter: I18NRouterService) => {
138 | const router = TestBed.get(Router);
139 |
140 | i18nRouter.init();
141 | i18nRouter.changeLanguage('tr');
142 |
143 | const fixture = TestBed.createComponent(TestBootstrapComponent);
144 | fixture.detectChanges();
145 |
146 | router.navigate(['/dil-secimi/en'])
147 | .then(() => {
148 | expect(router.url).toEqual('/dil-secimi/en');
149 | });
150 | })));
151 |
152 | it('should be able to translate routes w/`i18n-root` route `non-empty` string',
153 | fakeAsync(() => {
154 | const someRoutes: Routes = [
155 | {
156 | path: 'home',
157 | component: TestBootstrapComponent,
158 | children: [
159 | {
160 | path: '',
161 | component: TestComponent
162 | },
163 | {
164 | path: 'about',
165 | component: TestComponent
166 | }
167 | ],
168 | data: {
169 | i18n: {
170 | isRoot: true
171 | }
172 | }
173 | }
174 | ];
175 |
176 | const someTranslations = {
177 | en: {
178 | 'ROOT.HOME': 'home',
179 | 'ROOT.ABOUT': 'about'
180 | },
181 | tr: {
182 | 'ROOT.HOME': 'ana-sayfa',
183 | 'ROOT.ABOUT': 'hakkinda'
184 | }
185 | };
186 |
187 | const i18nRouterFactory = () => new I18NRouterStaticLoader({
188 | routes: someRoutes,
189 | translations: someTranslations
190 | });
191 |
192 | testModuleConfig(someRoutes, [{
193 | provide: I18NRouterLoader,
194 | useFactory: (i18nRouterFactory)
195 | }]);
196 |
197 | const router = TestBed.get(Router);
198 | const i18nRouter = TestBed.get(I18NRouterService);
199 |
200 | i18nRouter.init();
201 | i18nRouter.changeLanguage('tr');
202 |
203 | const fixture = TestBed.createComponent(TestBootstrapComponent);
204 | fixture.detectChanges();
205 |
206 | router.navigate(['/tr/ana-sayfa'])
207 | .then(() => {
208 | expect(router.url).toEqual('/tr/ana-sayfa');
209 | });
210 | }));
211 |
212 | it('should be able to translate routes w/o `i18n-root`',
213 | fakeAsync(() => {
214 | const someRoutes: Routes = [
215 | {
216 | path: 'home',
217 | component: TestBootstrapComponent,
218 | children: [
219 | {
220 | path: '',
221 | component: TestComponent
222 | },
223 | {
224 | path: 'about',
225 | component: TestComponent
226 | }
227 | ]
228 | }
229 | ];
230 |
231 | const someTranslations = {
232 | en: {
233 | HOME: 'home',
234 | ABOUT: 'about'
235 | },
236 | tr: {
237 | HOME: 'ana-sayfa',
238 | ABOUT: 'hakkinda'
239 | }
240 | };
241 |
242 | const i18nRouterFactory = () => new I18NRouterStaticLoader({
243 | routes: someRoutes,
244 | translations: someTranslations
245 | });
246 |
247 | testModuleConfig(someRoutes, [{
248 | provide: I18NRouterLoader,
249 | useFactory: (i18nRouterFactory)
250 | }]);
251 |
252 | const router = TestBed.get(Router);
253 | const i18nRouter = TestBed.get(I18NRouterService);
254 |
255 | i18nRouter.init();
256 | i18nRouter.changeLanguage('tr');
257 |
258 | const fixture = TestBed.createComponent(TestBootstrapComponent);
259 | fixture.detectChanges();
260 |
261 | router.navigate(['/ana-sayfa'])
262 | .then(() => {
263 | expect(router.url).toEqual('/ana-sayfa');
264 | });
265 | }));
266 | });
267 | });
268 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/core/tsconfig.es2015.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../../tsconfig.json",
3 | "compilerOptions": {
4 | "target": "es2015",
5 | "module": "es2015",
6 | "noImplicitAny": true,
7 | "suppressImplicitAnyIndexErrors": true,
8 | "importHelpers": true,
9 | "sourceMap": true,
10 | "inlineSources": true,
11 | "declaration": true,
12 | "removeComments": true,
13 | "stripInternal": true,
14 | "outDir": "../../../../.temp/packages/@ngx-i18n-router/core-es2015/",
15 | "rootDir": "./",
16 | "baseUrl": "",
17 | "typeRoots": ["../../../../node_modules/@types"],
18 | "types": ["node"],
19 | "lib": [
20 | "es2015",
21 | "dom"
22 | ]
23 | },
24 | "include": [],
25 | "files": ["./index.ts"],
26 | "angularCompilerOptions": {
27 | "annotateForClosureCompiler": true,
28 | "strictMetadataEmit": true,
29 | "skipTemplateCodegen": true,
30 | "flatModuleOutFile": "core.js",
31 | "flatModuleId": "@ngx-i18n-router/core",
32 | "genDir": "../../../../.temp/gen-dir/"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/core/tsconfig.es5.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.es2015.json",
3 | "compilerOptions": {
4 | "target": "es5",
5 | "outDir": "../../../../.temp/packages/@ngx-i18n-router/core-es5/"
6 | },
7 | "files": ["./index.ts"],
8 | "angularCompilerOptions": {
9 | "annotateForClosureCompiler": true,
10 | "strictMetadataEmit": true,
11 | "skipTemplateCodegen": true,
12 | "flatModuleOutFile": "core.js",
13 | "flatModuleId": "@ngx-i18n-router/core",
14 | "genDir": "../../../../.temp/gen-dir/"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/http-loader/README.md:
--------------------------------------------------------------------------------
1 | # @ngx-i18n-router/http-loader [](https://www.npmjs.com/package/@ngx-i18n-router/http-loader) [](https://www.npmjs.com/package/@ngx-i18n-router/http-loader)
2 | Loader for [ngx-i18n-router] that provides route translations using `http`
3 |
4 | [](https://circleci.com/gh/fulls1z3/ngx-i18n-router)
5 | [](https://codecov.io/gh/fulls1z3/ngx-i18n-router)
6 | [](https://github.com/facebook/jest)
7 | [](https://conventionalcommits.org)
8 | [](https://angular.io/styleguide)
9 |
10 | > Please support this project by simply putting a Github star. Share this library with friends on Twitter and everywhere else you can.
11 |
12 | **`@ngx-i18n-router/http-loader`** provides **route translations** to [@ngx-i18n-router/core] using `http`.
13 |
14 | #### NOTICE
15 | > This *[5.x.x] branch* is intented to work with `@angular v5.x.x`. If you're developing on a later release of **Angular**
16 | than `v5.x.x`, then you should probably choose the appropriate version of this library by visiting the *[master] branch*.
17 |
18 | ## Table of contents:
19 | - [Prerequisites](#prerequisites)
20 | - [Getting started](#getting-started)
21 | - [Installation](#installation)
22 | - [Examples](#examples)
23 | - [Related packages](#related-packages)
24 | - [Adding `@ngx-i18n-router/http-loader` to your project (SystemJS)](#adding-systemjs)
25 | - [Settings](#settings)
26 | - [Setting up `I18NRouterModule` to use `I18NRouterHttpLoader`](#setting-up-httploader)
27 | - [Translations object](#translations-object)
28 | - [License](#license)
29 |
30 | ## Prerequisites
31 | This library depends on `Angular v4.0.0`. Older versions contain outdated dependencies, might produce errors.
32 |
33 | Also, please ensure that you are using **`Typescript v2.5.3`** or higher.
34 |
35 | ## Getting started
36 | ### Installation
37 | You can install **`@ngx-i18n-router/http-loader`** using `npm`
38 | ```
39 | npm install @ngx-i18n-router/http-loader --save
40 | ```
41 |
42 | **Note**: You should have already installed [@ngx-i18n-router/core].
43 |
44 | ### Examples
45 | - [ng-seed/universal] and [fulls1z3/example-app] are officially maintained projects, showcasing common patterns and best
46 | practices for **`@ngx-i18n-router/http-loader`**.
47 |
48 | ### Related packages
49 | The following packages may be used in conjunction with **`@ngx-i18n-router/http-loader`**:
50 | - [@ngx-i18n-router/core]
51 |
52 | ### Adding `@ngx-i18n-router/http-loader` to your project (SystemJS)
53 | Add `map` for **`@ngx-i18n-router/http-loader`** in your `systemjs.config`
54 | ```javascript
55 | '@ngx-i18n-router/http-loader': 'node_modules/@ngx-i18n-router/http-loader/bundles/http-loader.umd.min.js'
56 | ```
57 |
58 | ## Settings
59 | ### Setting up `I18NRouterModule` to use `I18NRouterHttpLoader`
60 | #### routes.json
61 | ```json
62 | {
63 | "en": {
64 | "ROOT.ABOUT": "about",
65 | "ROOT.ABOUT.US": "us",
66 | "ROOT.ABOUT.BANANA": "banana",
67 | "ROOT.ABOUT.APPLE": "apple",
68 | "ROOT.ABOUT.APPLE.PEAR": "pear",
69 | "CHANGE_LANGUAGE": "change-language"
70 | },
71 | "tr": {
72 | "ROOT.ABOUT": "hakkinda",
73 | "ROOT.ABOUT.US": "biz",
74 | "ROOT.ABOUT.BANANA": "muz",
75 | "ROOT.ABOUT.APPLE": "elma",
76 | "ROOT.ABOUT.APPLE.PEAR": "armut",
77 | "CHANGE_LANGUAGE": "dil-secimi"
78 | }
79 | }
80 | ```
81 |
82 | #### app.module.ts
83 | ```TypeScript
84 | ...
85 | import { HttpClient } from '@angular/common/http';
86 | import { I18NRouterModule, I18NRouterLoader, I18NRouterHttpLoader, I18N_ROUTER_PROVIDERS, RAW_ROUTES } from '@ngx-i18n-router/core';
87 | ...
88 |
89 | export function i18nRouterFactory(http: HttpClient, rawRoutes: Routes): I18NRouterLoader {
90 | return new I18NRouterHttpLoader(http, '/routes.json', {routes: rawRoutes}); // FILE PATH || API ENDPOINT
91 | }
92 |
93 | ...
94 |
95 | @NgModule({
96 | declarations: [
97 | AppComponent,
98 | ...
99 | ],
100 | ...
101 | imports: [
102 | RouterModule.forRoot(routes),
103 | I18NRouterModule.forRoot(routes, [
104 | {
105 | provide: I18NRouterLoader,
106 | useFactory: (i18nRouterFactory),
107 | deps: [Http, RAW_ROUTES]
108 | }
109 | ]),
110 | ...
111 | ],
112 | ...
113 | providers: [
114 | I18N_ROUTER_PROVIDERS,
115 | ...
116 | ],
117 | ...
118 | bootstrap: [AppComponent]
119 | })
120 | ```
121 |
122 | `I18NRouterHttpLoader` has three parameters:
123 | - **http**: `Http` : Http instance
124 | - **path**: `string` : path to `JSON file`/`API endpoint`, to retrieve route translations from (*by default, `routes.json`*)
125 | - **providedSettings**: `I18NRouterSettings` : i18n-router settings
126 | - **routes**: `Routes`: raw routes
127 |
128 | ### Translations object
129 | You can find detailed information about the **data structure** and usage guidelines for the translations object [here](https://github.com/fulls1z3/ngx-i18n-router/tree/master/packages/@ngx-i18n-router/core#translations-object).
130 |
131 | Assuming that application settings are provided from `./routes.json`, adding the following data to configuration file will
132 | provide route translations to **`@ngx-i18n-router/core`** through **`http`**.
133 |
134 | #### routes.json
135 | ```json
136 | {
137 | "en": {
138 | "ROOT.ABOUT": "about",
139 | "ROOT.ABOUT.US": "us",
140 | "ROOT.ABOUT.BANANA": "banana",
141 | "ROOT.ABOUT.APPLE": "apple",
142 | "ROOT.ABOUT.APPLE.PEAR": "pear",
143 | "CHANGE_LANGUAGE": "change-language"
144 | },
145 | "tr": {
146 | "ROOT.ABOUT": "hakkinda",
147 | "ROOT.ABOUT.US": "biz",
148 | "ROOT.ABOUT.BANANA": "muz",
149 | "ROOT.ABOUT.APPLE": "elma",
150 | "ROOT.ABOUT.APPLE.PEAR": "armut",
151 | "CHANGE_LANGUAGE": "dil-secimi"
152 | }
153 | }
154 | ```
155 |
156 | > :+1: Well! **`@ngx-i18n-router/http-loader`** will now provide **route translations** to [@ngx-i18n-router/core] using
157 | `http`.
158 |
159 | ## License
160 | The MIT License (MIT)
161 |
162 | Copyright (c) 2018 [Burak Tasci]
163 |
164 | [master]: https://github.com/ngx-i18n-router/core/tree/master
165 | [5.x.x]: https://github.com/ngx-i18n-router/core/tree/5.x.x
166 | [ngx-i18n-router]: https://github.com/fulls1z3/ngx-i18n-router
167 | [@ngx-i18n-router/core]: https://github.com/fulls1z3/ngx-i18n-router/tree/master/packages/@ngx-i18n-router/core
168 | [ng-seed/universal]: https://github.com/ng-seed/universal
169 | [fulls1z3/example-app]: https://github.com/fulls1z3/example-app
170 | [Burak Tasci]: https://github.com/fulls1z3
171 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/http-loader/index.ts:
--------------------------------------------------------------------------------
1 | export * from './src/i18n-router.http-loader';
2 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/http-loader/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ngx-i18n-router/http-loader",
3 | "version": "5.0.0",
4 | "description": "Loader for ngx-i18n-router that provides route translations using http",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/fulls1z3/ngx-i18n-router.git"
8 | },
9 | "keywords": [
10 | "lazy loading",
11 | "localization",
12 | "i18n",
13 | "async",
14 | "plugin",
15 | "loader",
16 | "http",
17 | "localized router",
18 | "router",
19 | "i18n router",
20 | "angular"
21 | ],
22 | "author": {
23 | "name": "Burak Tasci",
24 | "email": "me@fulls1z3.com"
25 | },
26 | "license": "MIT",
27 | "bugs": {
28 | "url": "https://github.com/fulls1z3/ngx-i18n-router/issues"
29 | },
30 | "homepage": "https://github.com/fulls1z3/ngx-i18n-router/tree/master/packages/@ngx-i18n-router/http-loader#readme",
31 | "main": "./bundles/http-loader.umd.js",
32 | "module": "./@ngx-i18n-router/http-loader.es5.js",
33 | "es2015": "./@ngx-i18n-router/http-loader.js",
34 | "typings": "./http-loader.d.ts",
35 | "dependencies": {
36 | "tslib": "~1.8.1"
37 | },
38 | "peerDependencies": {
39 | "@angular/common": ">=5.0.0 <6.0.0",
40 | "@angular/core": ">=5.0.0 <6.0.0",
41 | "@angular/platform-browser-dynamic": ">=5.0.0 <6.0.0",
42 | "@angular/router": ">=5.0.0 <6.0.0",
43 | "rxjs": ">=5.5.0",
44 | "lodash": ">=4.17.4",
45 | "@ngx-i18n-router/core": ">=5.0.0 <6.0.0"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/http-loader/src/i18n-router.http-loader.ts:
--------------------------------------------------------------------------------
1 | // angular
2 | import { HttpClient } from '@angular/common/http';
3 | import { Routes } from '@angular/router';
4 |
5 | // libs
6 | import * as _ from 'lodash';
7 | import { I18NRouterLoader, I18NRouterSettings } from '@ngx-i18n-router/core';
8 |
9 | export class I18NRouterHttpLoader implements I18NRouterLoader {
10 | private _translations: any;
11 |
12 | get routes(): Routes {
13 | return _.map(this.providedSettings.routes, _.cloneDeep);
14 | }
15 |
16 | get translations(): any {
17 | return this._translations;
18 | }
19 |
20 | constructor(private readonly http: HttpClient,
21 | private readonly path: string = '/routes.json',
22 | private readonly providedSettings: I18NRouterSettings = {}) {
23 | }
24 |
25 | loadTranslations(): any {
26 | return new Promise((resolve, reject) => {
27 | this.http.get(this.path)
28 | .subscribe(res => {
29 | this._translations = res;
30 |
31 | return resolve(res);
32 | }, () => reject('Endpoint unreachable!'));
33 | });
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/http-loader/tests/i18n-router.http-loader.spec.ts:
--------------------------------------------------------------------------------
1 | // angular
2 | import { TestBed } from '@angular/core/testing';
3 | import { HttpClient } from '@angular/common/http';
4 | import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
5 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
6 | import { Route, Routes } from '@angular/router';
7 | import { RouterTestingModule } from '@angular/router/testing';
8 |
9 | // libs
10 | import { I18N_ROUTER_PROVIDERS, I18NRouterLoader, I18NRouterModule, I18NRouterService } from '@ngx-i18n-router/core';
11 |
12 | // module
13 | import { I18NRouterHttpLoader } from '../index';
14 |
15 | const testTranslations = {
16 | en: {
17 | 'ROOT.ABOUT': 'about',
18 | 'ROOT.ABOUT.US': 'us',
19 | 'ROOT.ABOUT.BANANA': 'banana',
20 | 'ROOT.ABOUT.APPLE': 'apple',
21 | 'ROOT.ABOUT.APPLE.PEAR': 'pear',
22 | 'ROOT.ABOUT.PLUM': 'plum',
23 | CHANGE_LANGUAGE: 'change-language'
24 | },
25 | tr: {
26 | 'ROOT.ABOUT': 'hakkinda',
27 | 'ROOT.ABOUT.US': 'biz',
28 | // "ROOT.ABOUT.BANANA": 'muz',
29 | 'ROOT.ABOUT.APPLE': 'elma',
30 | 'ROOT.ABOUT.APPLE.PEAR': 'armut',
31 | 'ROOT.ABOUT.PLUM': 'erik',
32 | CHANGE_LANGUAGE: 'dil-secimi'
33 | }
34 | };
35 | const testRoutes: Routes = [];
36 |
37 | export const testModuleConfig = (routes: Array = [], moduleOptions?: Array) => {
38 | TestBed.resetTestEnvironment();
39 |
40 | TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting())
41 | .configureTestingModule({
42 | imports: [
43 | HttpClientTestingModule,
44 | RouterTestingModule.withRoutes(routes),
45 | I18NRouterModule.forRoot(routes, moduleOptions)
46 | ],
47 | providers: [I18N_ROUTER_PROVIDERS]
48 | });
49 | };
50 |
51 | describe('@ngx-i18n-router/http-loader:',
52 | () => {
53 | describe('I18NRouterHttpLoader',
54 | () => {
55 | it('should be able to provide `I18NRouterHttpLoader`',
56 | () => {
57 | const i18nRouterFactory = (http: HttpClient) => new I18NRouterHttpLoader(http);
58 |
59 | testModuleConfig(testRoutes, [{
60 | provide: I18NRouterLoader,
61 | useFactory: (i18nRouterFactory),
62 | deps: [HttpClient]
63 | }]);
64 |
65 | const i18nRouter = TestBed.get(I18NRouterService);
66 |
67 | expect(I18NRouterHttpLoader).toBeDefined();
68 | expect(i18nRouter.loader).toBeDefined();
69 | expect(i18nRouter.loader instanceof I18NRouterHttpLoader).toBeTruthy();
70 |
71 | const httpMock = TestBed.get(HttpTestingController);
72 | httpMock.expectOne({method: 'GET', url: '/routes.json'}).flush(testTranslations);
73 | httpMock.verify();
74 | });
75 |
76 | it('should be able to retrieve route translations from the specified `path`',
77 | () => {
78 | const i18nRouterFactory = (http: HttpClient) => new I18NRouterHttpLoader(http, '/api/routes', {routes: testRoutes});
79 |
80 | testModuleConfig(testRoutes, [{
81 | provide: I18NRouterLoader,
82 | useFactory: (i18nRouterFactory),
83 | deps: [HttpClient]
84 | }]);
85 |
86 | const i18nRouter = TestBed.get(I18NRouterService);
87 |
88 | i18nRouter.loader.loadTranslations()
89 | .then((res: any) => {
90 | expect(res).toEqual(testTranslations);
91 | });
92 |
93 | const httpMock = TestBed.get(HttpTestingController);
94 | const reqs = httpMock.match('/api/routes');
95 |
96 | for (const req of reqs)
97 | req.flush(testTranslations);
98 |
99 | httpMock.verify();
100 | });
101 |
102 | it('should throw w/o a valid `endpoint`',
103 | (done: jest.DoneCallback) => {
104 | const i18nRouterFactory = (http: HttpClient) => new I18NRouterHttpLoader(http, '/api/wrong-routes', {routes: testRoutes});
105 |
106 | testModuleConfig(testRoutes, [{
107 | provide: I18NRouterLoader,
108 | useFactory: (i18nRouterFactory),
109 | deps: [HttpClient]
110 | }]);
111 |
112 | const i18nRouter = TestBed.get(I18NRouterService);
113 |
114 | i18nRouter.loader.loadTranslations()
115 | .catch(err => {
116 | expect(err).toEqual('Endpoint unreachable!');
117 | done();
118 | });
119 |
120 | const httpMock = TestBed.get(HttpTestingController);
121 | const reqs = httpMock.match('/api/wrong-routes');
122 |
123 | for (const req of reqs)
124 | req.flush({}, {status: 500, statusText: ''});
125 |
126 | httpMock.verify();
127 | });
128 | });
129 | });
130 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/http-loader/tsconfig.es2015.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../../tsconfig.json",
3 | "compilerOptions": {
4 | "target": "es2015",
5 | "module": "es2015",
6 | "noImplicitAny": true,
7 | "suppressImplicitAnyIndexErrors": true,
8 | "importHelpers": true,
9 | "sourceMap": true,
10 | "inlineSources": true,
11 | "declaration": true,
12 | "removeComments": true,
13 | "stripInternal": true,
14 | "outDir": "../../../../.temp/packages/@ngx-i18n-router/http-loader-es2015/",
15 | "rootDir": "./",
16 | "baseUrl": "",
17 | "paths": {
18 | "@ngx-i18n-router/core": ["../../../../dist/@ngx-i18n-router/core"]
19 | },
20 | "typeRoots": ["../../../../node_modules/@types"],
21 | "types": ["node"],
22 | "lib": [
23 | "es2015",
24 | "dom"
25 | ]
26 | },
27 | "include": [],
28 | "files": ["./index.ts"],
29 | "angularCompilerOptions": {
30 | "annotateForClosureCompiler": true,
31 | "strictMetadataEmit": true,
32 | "skipTemplateCodegen": true,
33 | "flatModuleOutFile": "http-loader.js",
34 | "flatModuleId": "@ngx-i18n-router/http-loader",
35 | "genDir": "../../../../.temp/gen-dir/"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/@ngx-i18n-router/http-loader/tsconfig.es5.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.es2015.json",
3 | "compilerOptions": {
4 | "target": "es5",
5 | "outDir": "../../../../.temp/packages/@ngx-i18n-router/http-loader-es5/"
6 | },
7 | "files": ["./index.ts"],
8 | "angularCompilerOptions": {
9 | "annotateForClosureCompiler": true,
10 | "strictMetadataEmit": true,
11 | "skipTemplateCodegen": true,
12 | "flatModuleOutFile": "http-loader.js",
13 | "flatModuleId": "@ngx-i18n-router/http-loader",
14 | "genDir": "../../../../.temp/gen-dir/"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/tools/build/build-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "@ngx-i18n-router": {
3 | "core": {
4 | "angularVersion": 4,
5 | "bundle": {
6 | "globals": {
7 | "@angular/router": "ng.router",
8 | "lodash": "_",
9 | "lodash/index": "_"
10 | },
11 | "external": []
12 | },
13 | "runTests": true
14 | },
15 | "http-loader": {
16 | "angularVersion": 4,
17 | "bundle": {
18 | "globals": {
19 | "@angular/router": "ng.router",
20 | "lodash": "_",
21 | "lodash/index": "_",
22 | "@ngx-i18n-router/core": "_ngxI18nRouter_core"
23 | },
24 | "external": [
25 | "rxjs/add/operator/map",
26 | "rxjs/add/operator/toPromise"
27 | ]
28 | },
29 | "runTests": true
30 | },
31 | "config-loader": {
32 | "angularVersion": 4,
33 | "bundle": {
34 | "globals": {
35 | "@angular/router": "ng.router",
36 | "lodash": "_",
37 | "lodash/index": "_",
38 | "@ngx-config/core": "_ngxConfig_core",
39 | "@ngx-i18n-router/core": "_ngxI18nRouter_core"
40 | },
41 | "external": []
42 | },
43 | "runTests": true
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tools/build/helpers.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 |
3 | export const NODE_MODULES = 'node_modules';
4 |
5 | export function root(args: any = ''): string {
6 | const ROOT = path.resolve(__dirname, '../..');
7 | args = [].slice.call(arguments, 0);
8 |
9 | return path.join.apply(path, [ROOT].concat(args));
10 | }
11 |
--------------------------------------------------------------------------------
/tools/build/inline-resources.ts:
--------------------------------------------------------------------------------
1 | import { readFile, readFileSync, writeFile } from 'fs';
2 | import * as path from 'path';
3 | import * as glob from 'glob';
4 |
5 | const inlineTemplate = (content: string, urlResolver: Function) =>
6 | content.replace(/templateUrl:\s*'([^']+?\.html)'/g, (m, templateUrl) => {
7 | const templateFile = urlResolver(templateUrl);
8 | const templateContent = readFileSync(templateFile, {encoding: 'utf-8'});
9 | const shortenedTemplate = (templateContent as string)
10 | .replace(/([\n\r]\s*)+/gm, ' ')
11 | .replace(/"/g, '\\"');
12 |
13 | return `template: "${shortenedTemplate}"`;
14 | });
15 |
16 | const inlineStyle = (content: string, urlResolver: Function) =>
17 | content.replace(/styleUrls:\s*(\[[\s\S]*?\])/gm, (m, styleUrls) => {
18 | // tslint:disable-next-line
19 | const urls = eval(styleUrls);
20 |
21 | const res = urls.map((styleUrl: string) => {
22 | const styleFile = urlResolver(styleUrl);
23 | const styleContent = readFileSync(styleFile, {encoding: 'utf-8'});
24 | const shortenedStyle = (styleContent as string)
25 | .replace(/([\n\r]\s*)+/gm, ' ')
26 | .replace(/"/g, '\\"');
27 |
28 | return `"${shortenedStyle}"`;
29 | });
30 |
31 | return `styles: [${res}]`;
32 | });
33 |
34 | const inlineResourcesFromString = (content: string, urlResolver: Function) => [
35 | inlineTemplate,
36 | inlineStyle
37 | ].reduce((res, fn) => fn(res, urlResolver), content);
38 |
39 | function promiseify(fn: any): any {
40 | return function(): any {
41 | const args = [].slice.call(arguments, 0);
42 |
43 | return new Promise((resolve, reject) => {
44 | // tslint:disable-next-line
45 | fn.apply(this, args.concat([(err: any, value: any) => {
46 | if (err)
47 | reject(err);
48 | else
49 | resolve(value);
50 | }]));
51 | });
52 | };
53 | }
54 |
55 | const readFileAsync = promiseify(readFile);
56 | const writeFileAsync = promiseify(writeFile);
57 |
58 | export const inlineResources = (projectPath: string) => {
59 | const files = glob.sync('**/*.ts', {cwd: projectPath});
60 |
61 | return Promise.all(files
62 | .map((filePath: string) => {
63 | const fullFilePath = path.join(projectPath, filePath);
64 |
65 | return readFileAsync(fullFilePath, 'utf-8')
66 | .then((content: string) => inlineResourcesFromString(content,
67 | (url: string) => path.join(path.dirname(fullFilePath), url)))
68 | .then((content: string) => writeFileAsync(fullFilePath, content))
69 | .catch((err: string) => {
70 | console.error('An error occured: ', err);
71 | });
72 | }));
73 | };
74 |
--------------------------------------------------------------------------------
/tools/build/rollup.ts:
--------------------------------------------------------------------------------
1 | import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
2 | import * as path from 'path';
3 | import * as glob from 'glob';
4 | import * as camelCase from 'camelcase';
5 | import { main as ngc } from '@angular/compiler-cli/src/main';
6 | import * as rollup from 'rollup';
7 | import * as commonJs from 'rollup-plugin-commonjs';
8 | import * as sourceMaps from 'rollup-plugin-sourcemaps';
9 | import * as resolve from 'rollup-plugin-node-resolve';
10 | import * as uglify from 'rollup-plugin-uglify';
11 |
12 | import { NODE_MODULES, root } from './helpers';
13 | import { inlineResources } from './inline-resources';
14 |
15 | const compilationFolder = root('.temp');
16 | let globals = {
17 | tslib: 'tslib',
18 | '@angular/core': 'ng.core'
19 | };
20 |
21 | const relativeCopy = (fileGlob: string, from: string, to: string) => {
22 | return new Promise((res, reject) => {
23 | glob(fileGlob, {
24 | cwd: from,
25 | nodir: true
26 | }, (err: string, files: Array) => {
27 | if (err)
28 | reject(err);
29 |
30 | for (const file of files) {
31 | if (file.indexOf(NODE_MODULES) >= 0)
32 | continue;
33 |
34 | const origin = path.join(from, file);
35 | const destination = path.join(to, file);
36 | const data = readFileSync(origin, 'utf-8');
37 |
38 | recursiveMkDir(path.dirname(destination));
39 | writeFileSync(destination, data);
40 | }
41 |
42 | res();
43 | });
44 | });
45 | };
46 |
47 | const recursiveMkDir = (dir: string) => {
48 | if (!existsSync(dir)) {
49 | recursiveMkDir(path.dirname(dir));
50 | mkdirSync(dir);
51 | }
52 | };
53 |
54 | import * as buildConfig from './build-config.json';
55 |
56 | const build = (group: string, item: string, settings: any) => {
57 | const paths = {
58 | src: root(`packages/${group}/${item}`),
59 | temp: path.join(compilationFolder, `packages/${group}/${item}`),
60 | es2015: path.join(compilationFolder, `packages/${group}/${item}-es2015`),
61 | es5: path.join(compilationFolder, `packages/${group}/${item}-es5`),
62 | dist: root(`dist/${group}/${item}`)
63 | };
64 |
65 | globals = {
66 | ...globals,
67 | ...settings.bundle.globals
68 | };
69 | const external = settings.bundle.external
70 | ? settings.bundle.external.concat(Object.keys(globals))
71 | : Object.keys(globals);
72 |
73 | return Promise.resolve()
74 | .then(() => relativeCopy('**/*', paths.src, paths.temp)
75 | .then(() => inlineResources(paths.temp))
76 | // tslint:disable-next-line
77 | .then(() => console.log(`>>> ${group}/${item}: Inlining succeeded`))
78 | )
79 | .then(() => ngc(['--project', `${paths.temp}/tsconfig.es2015.json`]))
80 | .then(exitCode => new Promise((res, reject) => {
81 | exitCode === 0
82 | ? res()
83 | : reject();
84 | }))
85 | // tslint:disable-next-line
86 | .then(() => console.log(`>>> ${group}/${item}: ES2015 compilation succeeded`))
87 | .then(() => ngc(['--project', `${paths.temp}/tsconfig.es5.json`]))
88 | .then(exitCode => new Promise((res, reject) => {
89 | exitCode === 0
90 | ? res()
91 | : reject();
92 | }))
93 | // tslint:disable-next-line
94 | .then(() => console.log(`>>> ${group}/${item}: ES5 compilation succeeded`))
95 | .then(() => Promise.resolve()
96 | .then(() => relativeCopy('**/*.d.ts', paths.es2015, paths.dist))
97 | .then(() => relativeCopy('**/*.metadata.json', paths.es2015, paths.dist))
98 | // tslint:disable-next-line
99 | .then(() => console.log(`>>> ${group}/${item}: Typings and metadata copy succeeded`))
100 | )
101 | .then(() => {
102 | const es5Entry = path.join(paths.es5, `${settings.angularVersion > 2 ? item : 'index'}.js`);
103 | const es2015Entry = path.join(paths.es2015, `${settings.angularVersion > 2 ? item : 'index'}.js`);
104 | const rollupBaseConfig = {
105 | moduleName: `${camelCase(group.replace(/@/g, ''))}.${camelCase(item)}`,
106 | sourceMap: true,
107 | globals,
108 | external,
109 | plugins: [
110 | resolve({
111 | module: true,
112 | jsnext: true
113 | }),
114 | commonJs({
115 | include: ['node_modules/rxjs/**']
116 | }),
117 | sourceMaps()
118 | ]
119 | };
120 |
121 | const umdConfig = {
122 | ...rollupBaseConfig,
123 | entry: es5Entry,
124 | dest: path.join(paths.dist, 'bundles', `${item}.umd.js`),
125 | format: 'umd'
126 | };
127 |
128 | const minUmdConfig = {
129 | ...rollupBaseConfig,
130 | entry: es5Entry,
131 | dest: path.join(paths.dist, 'bundles', `${item}.umd.min.js`),
132 | format: 'umd',
133 | plugins: rollupBaseConfig.plugins.concat([uglify({})])
134 | };
135 |
136 | const es5config = {
137 | ...rollupBaseConfig,
138 | entry: es5Entry,
139 | dest: path.join(paths.dist, `${group}/${item}.es5.js`),
140 | format: 'es'
141 | };
142 |
143 | const es2015config = {
144 | ...rollupBaseConfig,
145 | entry: es2015Entry,
146 | dest: path.join(paths.dist, `${group}/${item}.js`),
147 | format: 'es'
148 | };
149 |
150 | const bundles = [
151 | umdConfig,
152 | minUmdConfig,
153 | es5config,
154 | es2015config
155 | ]
156 | .map(options => rollup.rollup(options)
157 | .then((bundle: any) => bundle.write(options)));
158 |
159 | return Promise.all(bundles)
160 | // tslint:disable-next-line
161 | .then(() => console.log(`>>> ${group}/${item}: All bundles generated successfully`));
162 | })
163 | .then(() => Promise.resolve()
164 | .then(() => relativeCopy('LICENSE', root(), paths.dist))
165 | .then(() => relativeCopy('package.json', paths.src, paths.dist))
166 | .then(() => relativeCopy('README.md', paths.src, paths.dist))
167 | // tslint:disable-next-line
168 | .then(() => console.log(`>>> ${group}/${item}: Package files copy succeeded`))
169 | // tslint:disable-next-line
170 | .then(() => console.log(`\n`))
171 | )
172 | .catch(e => {
173 | console.error(`>>> ${group}/${item}: Build failed, see below for errors\n`);
174 | console.error(e);
175 | process.exit(1);
176 | });
177 | };
178 |
179 | const libs = [];
180 |
181 | for (const group of Object.keys(buildConfig))
182 | if (group !== '__once__')
183 | for (const item of Object.keys(buildConfig[group]))
184 | libs.push({
185 | group,
186 | item,
187 | settings: buildConfig[group][item]
188 | });
189 | else
190 | libs.push({
191 | group: '',
192 | item: Object.keys(buildConfig[group])[0],
193 | settings: buildConfig[group][Object.keys(buildConfig[group])[0]]
194 | });
195 |
196 | const toSeries = (series: any) => series
197 | .reduce((promise: Promise, fn: Function) => promise
198 | .then((res: any) => fn()
199 | .then(Array.prototype.concat.bind(res))), Promise.resolve([]));
200 |
201 | const builds = libs
202 | .map((lib: any) => () => build(lib.group, lib.item, lib.settings));
203 |
204 | toSeries(builds);
205 |
--------------------------------------------------------------------------------
/tools/test/jest.setup.ts:
--------------------------------------------------------------------------------
1 | import 'jest-preset-angular';
2 |
3 | const mock = () => {
4 | let storage = {};
5 |
6 | return {
7 | getItem: (key: string) => key in storage ? (storage as any)[key] : undefined,
8 | setItem: (key: string, value: any) => (storage as any)[key] = value || '',
9 | removeItem: (key: string) => delete (storage as any)[key],
10 | clear: () => storage = {}
11 | };
12 | };
13 |
14 | Object.defineProperty(window, 'CSS', {value: mock()});
15 | Object.defineProperty(window, 'localStorage', {value: mock()});
16 | Object.defineProperty(window, 'sessionStorage', {value: mock()});
17 |
18 | Object.defineProperty(document, 'doctype', {
19 | value: ''
20 | });
21 |
22 | Object.defineProperty(window, 'getComputedStyle', {
23 | value: () => {
24 | return {
25 | display: 'none',
26 | appearance: ['-webkit-appearance']
27 | };
28 | }
29 | });
30 |
--------------------------------------------------------------------------------
/tools/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "../tslint.json"
4 | ],
5 | "rules": {
6 | "no-implicit-dependencies": [
7 | true,
8 | "dev"
9 | ]
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/tools/typings.d.ts:
--------------------------------------------------------------------------------
1 | // custom typings
2 | declare module '*' {
3 | let json: any;
4 | export = json;
5 | }
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "moduleResolution": "node",
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "importHelpers": true,
9 | "baseUrl": "",
10 | "paths": {
11 | "@ngx-i18n-router/core": ["./packages/@ngx-i18n-router/core"]
12 | },
13 | "lib": [
14 | "es2017",
15 | "dom"
16 | ]
17 | },
18 | "include": [
19 | "tools/**/*.ts",
20 | "packages/**/*.ts"
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "extends": [
6 | "angular-tslint-rules"
7 | ],
8 | "rules": {
9 | "pipe-naming": [
10 | true,
11 | "camelCase",
12 | "config"
13 | ]
14 | }
15 | }
16 |
--------------------------------------------------------------------------------