Full Code of Romanchuk/angular-i18next for AI

master be47b2b66485 cached
137 files
219.4 KB
66.5k tokens
181 symbols
1 requests
Download .txt
Showing preview only (256K chars total). Download the full file or copy to clipboard to get everything.
Repository: Romanchuk/angular-i18next
Branch: master
Commit: be47b2b66485
Files: 137
Total size: 219.4 KB

Directory structure:
gitextract_w52vdt87/

├── .cleandir.sh
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       └── static.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode/
│   ├── extensions.json
│   ├── launch.json
│   └── settings.json
├── CHANGELOG.md
├── FUNDING.yml
├── LICENSE
├── README.md
├── README_DEPRECATED.md
├── apps/
│   └── angular-i18next-demo/
│       ├── .browserslistrc
│       ├── .eslintrc.json
│       ├── jest.config.ts
│       ├── project.json
│       ├── src/
│       │   ├── app/
│       │   │   ├── app.component.css
│       │   │   ├── app.component.html
│       │   │   ├── app.component.ts
│       │   │   ├── app.config.server.ts
│       │   │   ├── app.config.ts
│       │   │   ├── app.routes.server.ts
│       │   │   ├── app.routes.ts
│       │   │   ├── content/
│       │   │   │   ├── access-denied/
│       │   │   │   │   ├── access-denied.component.html
│       │   │   │   │   └── access-denied.component.ts
│       │   │   │   ├── simple-demo.component.html
│       │   │   │   └── simple-demo.component.ts
│       │   │   ├── features/
│       │   │   │   └── rich_form_feature/
│       │   │   │       ├── rich-form.component.html
│       │   │   │       ├── rich-form.component.ts
│       │   │   │       └── rich-form.model.ts
│       │   │   ├── i18next.options.ts
│       │   │   └── structure/
│       │   │       ├── app-error.component.html
│       │   │       ├── app-error.component.ts
│       │   │       ├── app-footer.component.html
│       │   │       ├── app-footer.component.ts
│       │   │       ├── app-header.component.html
│       │   │       ├── app-header.component.ts
│       │   │       └── header-controls/
│       │   │           ├── header.language.component.html
│       │   │           └── header.language.component.ts
│       │   ├── assets/
│       │   │   ├── .gitkeep
│       │   │   └── ng-validation.css
│       │   ├── environments/
│       │   │   ├── environment.prod.ts
│       │   │   └── environment.ts
│       │   ├── index.html
│       │   ├── lib/
│       │   │   └── validation/
│       │   │       ├── services/
│       │   │       │   └── ValidationDirtyChecker.ts
│       │   │       └── validators/
│       │   │           ├── ArrayValidators.js
│       │   │           ├── ArrayValidators.ts
│       │   │           ├── ConditionalValidator.js
│       │   │           └── ConditionalValidator.ts
│       │   ├── locales/
│       │   │   ├── en.error.json
│       │   │   ├── en.feature.rich_form.json
│       │   │   ├── en.translation.json
│       │   │   ├── en.validation.json
│       │   │   ├── ru.error.json
│       │   │   ├── ru.feature.rich_form.json
│       │   │   ├── ru.translation.json
│       │   │   └── ru.validation.json
│       │   ├── main.server.ts
│       │   ├── main.ts
│       │   ├── server.ts
│       │   ├── styles.css
│       │   └── test-setup.ts
│       ├── tsconfig.app.json
│       ├── tsconfig.editor.json
│       ├── tsconfig.json
│       ├── tsconfig.server.json
│       └── tsconfig.spec.json
├── decorate-angular-cli.js
├── ecosystem.config.js
├── jest.config.ts
├── jest.preset.js
├── libs/
│   └── angular-i18next/
│       ├── .eslintrc.json
│       ├── CHANGELOG.md
│       ├── README.md
│       ├── forms/
│       │   ├── ng-package.json
│       │   └── src/
│       │       ├── components/
│       │       │   └── validation-message.component.ts
│       │       ├── directives/
│       │       │   └── validation-message.directive.ts
│       │       ├── models.ts
│       │       └── public_api.ts
│       ├── jest.config.ts
│       ├── ng-package.json
│       ├── package.json
│       ├── postinstall.js
│       ├── project.json
│       ├── src/
│       │   ├── .eslintrc.json
│       │   ├── index.ts
│       │   ├── lib/
│       │   │   ├── I18NextErrorHandlingStrategies.ts
│       │   │   ├── I18NextEvents.ts
│       │   │   ├── I18NextLoadResult.ts
│       │   │   ├── I18NextModuleParams.ts
│       │   │   ├── index.ts
│       │   │   ├── interpolation.ts
│       │   │   ├── models.ts
│       │   │   ├── module.ts
│       │   │   ├── namespace.resolver.ts
│       │   │   ├── namespaces.guard.ts
│       │   │   ├── pipes/
│       │   │   │   ├── i18next-cap.pipe.ts
│       │   │   │   ├── i18next-eager.pipe.ts
│       │   │   │   ├── i18next-format.pipe.ts
│       │   │   │   └── i18next.pipe.ts
│       │   │   ├── provider.ts
│       │   │   ├── provider.utils.ts
│       │   │   ├── services/
│       │   │   │   ├── i18next-title.ts
│       │   │   │   ├── i18next.service.ts
│       │   │   │   ├── translation.events.ts
│       │   │   │   └── translation.service.ts
│       │   │   └── tokens.ts
│       │   ├── test-setup.ts
│       │   └── tests/
│       │       ├── module/
│       │       │   └── module.spec.ts
│       │       ├── pipes/
│       │       │   ├── I18NextEagerPipe.spec.ts
│       │       │   └── I18NextPipe.spec.ts
│       │       ├── projectTests/
│       │       │   ├── project.component.ts
│       │       │   └── projectTests.spec.ts
│       │       ├── provider/
│       │       │   └── provider.spec.ts
│       │       ├── service/
│       │       │   └── I18NextService.spec.ts
│       │       └── setup.ts
│       ├── ssr/
│       │   ├── ng-package.json
│       │   └── src/
│       │       ├── provider.ssr.ts
│       │       └── public_api.ts
│       ├── testing/
│       │   ├── ng-package.json
│       │   └── src/
│       │       ├── mock.service.ts
│       │       ├── provider.ts
│       │       └── public_api.ts
│       ├── tsconfig.json
│       ├── tsconfig.lib.json
│       └── tsconfig.spec.json
├── migrations.json
├── nx.json
├── package.json
├── tools/
│   └── tsconfig.tools.json
├── tsconfig.base.json
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .cleandir.sh
================================================
echo Cleaning up project directory...
rm -rf node_modules dist build coverage src/**/*.d.ts src/**/*.js src/**/*.js.map src/**/*.metadata.json

================================================
FILE: .editorconfig
================================================
# Editor configuration, see http://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
max_line_length = off
trim_trailing_whitespace = false


================================================
FILE: .eslintignore
================================================
node_modules


================================================
FILE: .eslintrc.json
================================================
{
  "root": true,
  "ignorePatterns": ["**/*"],
  "plugins": ["@nx"],
  "overrides": [
    {
      "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
      "rules": {
        "@nx/enforce-module-boundaries": [
          "error",
          {
            "enforceBuildableLibDependency": true,
            "allow": [],
            "depConstraints": [
              {
                "sourceTag": "*",
                "onlyDependOnLibsWithTags": ["*"]
              }
            ]
          }
        ]
      }
    },
    {
      "files": ["*.ts", "*.tsx"],
      "extends": ["plugin:@nx/typescript"],
      "rules": {}
    },
    {
      "files": ["*.js", "*.jsx"],
      "extends": ["plugin:@nx/javascript"],
      "rules": {}
    }
  ]
}


================================================
FILE: .github/FUNDING.yml
================================================
custom: ["https://www.paypal.com/paypalme2/sergeyromanchuk/10USD"]


================================================
FILE: .github/workflows/static.yml
================================================
# Simple workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages

on:
  # Runs on pushes targeting the default branch
  push:
    branches: ["master"]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write

# Allow one concurrent deployment
concurrency:
  group: "pages"
  cancel-in-progress: true

jobs:
  # Single deploy job since we're just deploying
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [22.13.x]
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Setup Pages
        uses: actions/configure-pages@v5
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: npm run test
      - run: npm run build
      - run: npm run prepare:demo
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: './dist/angular-i18next-demo/browser'
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4


================================================
FILE: .gitignore
================================================
# See http://help.github.com/ignore-files/ for more about ignoring files.

# compiled output
dist
/tmp
/out-tsc

# dependencies
node_modules

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings

# System Files
.DS_Store
Thumbs.db

.angular

.nx


================================================
FILE: .prettierignore
================================================
# Add files here to ignore them from prettier formatting

/dist
/coverage

/.nx/cache
.angular


================================================
FILE: .prettierrc
================================================
{
  "singleQuote": true
}


================================================
FILE: .vscode/extensions.json
================================================
{
  "recommendations": [
    "angular.ng-template",
    "nrwl.angular-console",
    "esbenp.prettier-vscode",
    "firsttris.vscode-jest-runner",
    "dbaeumer.vscode-eslint",
    "github.vscode-github-actions"
  ]
}


================================================
FILE: .vscode/launch.json
================================================
{
  "version": "1.0.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Jest: current file",
      //"env": { "NODE_ENV": "test" },
      "program": "${workspaceFolder}/node_modules/.bin/jest",
      "args": ["${fileBasenameNoExtension}", "--config", "jest.config.ts"],
      "console": "integratedTerminal",
      "windows": {
        "program": "${workspaceFolder}/node_modules/jest/bin/jest"
      }
    }
  ]
}


================================================
FILE: .vscode/settings.json
================================================
// Place your settings in this file to overwrite default and user settings.
{
  "search.exclude": {
    "coverage": true,
    "dist": true,
    "build": true
  },
  "typescript.tsdk": "node_modules\\typescript\\lib"
}


================================================
FILE: CHANGELOG.md
================================================
# [10.3.0](https://github.com/Romanchuk/angular-i18next/compare/v10.3.0-0...v10.3.0) (2021-06-15)



# [10.3.0-0](https://github.com/Romanchuk/angular-i18next/compare/v10.2.0...v10.3.0-0) (2021-06-15)



# [10.2.0](https://github.com/Romanchuk/angular-i18next/compare/v10.2.0-0...v10.2.0) (2021-05-12)



# [10.2.0-0](https://github.com/Romanchuk/angular-i18next/compare/v10.1.0...v10.2.0-0) (2021-05-12)


### Features

* i18next v20+ support ([0327a7c](https://github.com/Romanchuk/angular-i18next/commit/0327a7c9f35140f0c8e098d9d1528b6e7303a8d0))



# [10.1.0](https://github.com/Romanchuk/angular-i18next/compare/v10.1.0-0...v10.1.0) (2021-03-01)



# [10.1.0-0](https://github.com/Romanchuk/angular-i18next/compare/v10.0.1...v10.1.0-0) (2021-03-01)


### Bug Fixes

* **I18NextEagerPipe:** ensure changing PipeOptions returns correct translated value a not cached one with different PipeOptions but same key ([4a6d375](https://github.com/Romanchuk/angular-i18next/commit/4a6d375181dda41399c58f7644b97d3755acf84f))



## [10.0.1](https://github.com/Romanchuk/angular-i18next/compare/v10.0.1-beta...v10.0.1) (2020-12-21)



## [10.0.1-beta](https://github.com/Romanchuk/angular-i18next/compare/v10.0.0...v10.0.1-beta) (2020-12-21)



# [10.0.0](https://github.com/Romanchuk/angular-i18next/compare/v10.0.0-2...v10.0.0) (2020-07-06)



# [10.0.0-2](https://github.com/Romanchuk/angular-i18next/compare/v10.0.0-1...v10.0.0-2) (2020-07-06)



# [10.0.0-1](https://github.com/Romanchuk/angular-i18next/compare/v10.0.0-0...v10.0.0-1) (2020-07-06)



# [10.0.0-0](https://github.com/Romanchuk/angular-i18next/compare/v9.0.1...v10.0.0-0) (2020-07-06)



## [9.0.1](https://github.com/Romanchuk/angular-i18next/compare/v9.0.0...v9.0.1) (2020-02-25)


### Bug Fixes

* pass translate options ([4cfe42c](https://github.com/Romanchuk/angular-i18next/commit/4cfe42c))



# [9.0.0](https://github.com/Romanchuk/angular-i18next/compare/v8.1.0-beta.3...v9.0.0) (2020-02-20)



# [8.1.0-beta.3](https://github.com/Romanchuk/angular-i18next/compare/v8.1.0-beta.2...v8.1.0-beta.3) (2020-02-20)



# [8.1.0-beta.2](https://github.com/Romanchuk/angular-i18next/compare/v8.1.0-beta.1...v8.1.0-beta.2) (2020-02-20)



# [8.1.0-beta.1](https://github.com/Romanchuk/angular-i18next/compare/v8.1.0-beta...v8.1.0-beta.1) (2020-02-20)


### Features

* improved typings ([214e35d](https://github.com/Romanchuk/angular-i18next/commit/214e35d))



# [8.1.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v7.2.0-beta...v8.1.0-beta) (2020-02-20)



## [8.0.1](https://github.com/Romanchuk/angular-i18next/compare/v8.0.1-beta.0...v8.0.1) (2020-02-18)



## [8.0.1-beta.0](https://github.com/Romanchuk/angular-i18next/compare/v8.0.1-beta...v8.0.1-beta.0) (2020-02-18)



## [8.0.1-beta](https://github.com/Romanchuk/angular-i18next/compare/v8.0.0...v8.0.1-beta) (2020-02-18)



# [8.0.0](https://github.com/Romanchuk/angular-i18next/compare/v8.0.0-beta.1...v8.0.0) (2020-02-14)



# [8.0.0-beta.1](https://github.com/Romanchuk/angular-i18next/compare/v8.0.0-beta...v8.0.0-beta.1) (2020-02-13)



# [8.0.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v7.2.0-beta...v8.0.0-beta) (2020-02-13)



# [7.2.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v7.0.0...v7.2.0-beta) (2020-01-28)


### Bug Fixes

* I18NextEagerPipe ([8dbefe1](https://github.com/Romanchuk/angular-i18next/commit/8dbefe1))



# [7.0.0](https://github.com/Romanchuk/angular-i18next/compare/v6.1.0...v7.0.0) (2019-06-05)



# [6.1.0](https://github.com/Romanchuk/angular-i18next/compare/v6.1.0-beta...v6.1.0) (2019-05-27)



# [6.1.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v6.0.1...v6.1.0-beta) (2019-05-25)



## [6.0.1](https://github.com/Romanchuk/angular-i18next/compare/v6.0.0...v6.0.1) (2019-03-11)



# [6.0.0](https://github.com/Romanchuk/angular-i18next/compare/v6.0.0-beta.0...v6.0.0) (2019-02-10)



# [6.0.0-beta.0](https://github.com/Romanchuk/angular-i18next/compare/v6.0.0-beta...v6.0.0-beta.0) (2019-02-10)



# [6.0.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v5.0.6...v6.0.0-beta) (2019-02-10)



## [5.0.6](https://github.com/Romanchuk/angular-i18next/compare/v5.0.5...v5.0.6) (2018-12-03)



## [5.0.5](https://github.com/Romanchuk/angular-i18next/compare/v5.0.4...v5.0.5) (2018-12-03)



## [5.0.4](https://github.com/Romanchuk/angular-i18next/compare/v5.0.3...v5.0.4) (2018-12-03)



## [5.0.3](https://github.com/Romanchuk/angular-i18next/compare/v5.0.2...v5.0.3) (2018-12-03)



## [5.0.2](https://github.com/Romanchuk/angular-i18next/compare/v5.0.1...v5.0.2) (2018-12-03)


### Bug Fixes

* package.json ([54a8c37](https://github.com/Romanchuk/angular-i18next/commit/54a8c37))



## [5.0.1](https://github.com/Romanchuk/angular-i18next/compare/v5.0.0...v5.0.1) (2018-11-28)



# [5.0.0](https://github.com/Romanchuk/angular-i18next/compare/v5.0.0-beta2...v5.0.0) (2018-11-28)



# [5.0.0-beta2](https://github.com/Romanchuk/angular-i18next/compare/v5.0.0-beta...v5.0.0-beta2) (2018-11-28)



# [5.0.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v4.0.0...v5.0.0-beta) (2018-11-28)


### Bug Fixes

* docs ([220a0b8](https://github.com/Romanchuk/angular-i18next/commit/220a0b8))



<a name="4.0.0"></a>
# [4.0.0](https://github.com/Romanchuk/angular-i18next/compare/v4.0.0-beta...v4.0.0) (2018-06-25)

In v4 passed through most of i18next api methods

1. Update angular to v6+
2. Update rxjs to v6.2.0+


<a name="4.0.0-beta"></a>
# [4.0.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v3.4.2...v4.0.0-beta) (2018-06-11)



<a name="3.4.2"></a>
## [3.4.2](https://github.com/Romanchuk/angular-i18next/compare/v3.4.1...v3.4.2) (2018-05-05)



<a name="3.4.1"></a>
## [3.4.1](https://github.com/Romanchuk/angular-i18next/compare/v3.4.0...v3.4.1) (2018-04-29)

- default formater fixes


<a name="3.4.0"></a>
# [3.4.0](https://github.com/Romanchuk/angular-i18next/compare/v3.3.0...v3.4.0) (2018-04-29)

- i18next v11 support 
- fix: [format pipe](https://github.com/Romanchuk/angular-i18next/issues/15)


<a name="3.3.0"></a>
# [3.3.0](https://github.com/Romanchuk/angular-i18next/compare/v3.3.0-beta.2...v3.3.0) (2018-03-12)

- added umd bundle
- comments cleanup
- updated dev dependencies

<a name="3.3.0-beta.2"></a>
# [3.3.0-beta.2](https://github.com/Romanchuk/angular-i18next/compare/v3.3.0-beta.1...v3.3.0-beta.2) (2018-03-12)



<a name="3.3.0-beta.1"></a>
# [3.3.0-beta.1](https://github.com/Romanchuk/angular-i18next/compare/v3.2.0...v3.3.0-beta.1) (2018-02-04)



<a name="3.2.0"></a>
# [3.2.0](https://github.com/Romanchuk/angular-i18next/compare/v3.1.1...v3.2.0) (2018-01-17)

### Bug Fixes
* [aot build failed](Romanchuk/angular-i18next#10)

### Breaking changes

Removed parameter 'localizeTitle' from forRoot method.
You need to manually resolve Title as I18NextTitle for same behavior.


<a name="3.1.1"></a>
## [3.1.1](https://github.com/Romanchuk/angular-i18next/compare/v3.1.0...v3.1.1) (2018-01-01)


### Bug Fixes

* bug namespace fallback ([a16b067](https://github.com/Romanchuk/angular-i18next/commit/a16b067))
* conventional-github-releaser run ([df3bb84](https://github.com/Romanchuk/angular-i18next/commit/df3bb84))



<a name="3.1.0"></a>
# [3.1.0](https://github.com/Romanchuk/angular-i18next/compare/v3.0.0...v3.1.0) (2017-12-22)

It is possible to pass array of namespaces (or scopes). [Key would fallback](https://www.i18next.com/api.html#t) to next namespace in array if the previous failed to resolve.

`[feature.validators:key, validators:key]`
```typescript
{
  provide: I18NEXT_NAMESPACE,
  useValue: ['feature.validators', 'validators']
}
```

<a name="3.0.0"></a>
# [3.0.0](https://github.com/Romanchuk/angular-i18next/compare/v3.0.0-alpha.2...v3.0.0) (2017-12-15)



<a name="3.0.0-alpha.2"></a>
# [3.0.0-alpha.2](https://github.com/Romanchuk/angular-i18next/compare/v3.0.0-alpha...v3.0.0-alpha.2) (2017-12-05)



<a name="3.0.0-alpha"></a>
# [3.0.0-alpha](https://github.com/Romanchuk/angular-i18next/compare/v2.0.0...v3.0.0-alpha) (2017-11-27)



<a name="2.0.0"></a>
# [2.0.0](https://github.com/Romanchuk/angular-i18next/compare/v2.0.0-beta2...v2.0.0) (2017-11-14)



<a name="2.0.0-beta2"></a>
# [2.0.0-beta2](https://github.com/Romanchuk/angular-i18next/compare/v2.0.0-beta...v2.0.0-beta2) (2017-11-05)



<a name="2.0.0-beta"></a>
# [2.0.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v1.1.0...v2.0.0-beta) (2017-11-05)



<a name="1.1.0"></a>
# [1.1.0](https://github.com/Romanchuk/angular-i18next/compare/v1.0.2...v1.1.0) (2017-11-04)



<a name="1.0.2"></a>
## [1.0.2](https://github.com/Romanchuk/angular-i18next/compare/v1.0.1...v1.0.2) (2017-09-22)



<a name="1.0.1"></a>
## [1.0.1](https://github.com/Romanchuk/angular-i18next/compare/v1.0.0...v1.0.1) (2017-09-21)



<a name="1.0.0"></a>
# [1.0.0](https://github.com/Romanchuk/angular-i18next/compare/v0.2.4...v1.0.0) (2017-09-21)



<a name="0.2.4"></a>
## [0.2.4](https://github.com/Romanchuk/angular-i18next/compare/v0.2.3...v0.2.4) (2017-06-29)



<a name="0.2.3"></a>
## [0.2.3](https://github.com/Romanchuk/angular-i18next/compare/v0.2.2...v0.2.3) (2017-06-29)



<a name="0.2.2"></a>
## [0.2.2](https://github.com/Romanchuk/angular-i18next/compare/v0.2.1...v0.2.2) (2017-06-29)


### Bug Fixes

* **I18NextService:** context-safe calls of i18next methods ([455a07d](https://github.com/Romanchuk/angular-i18next/commit/455a07d))



<a name="0.2.1"></a>
## [0.2.1](https://github.com/Romanchuk/angular-i18next/compare/v0.2.0...v0.2.1) (2017-06-29)


### Bug Fixes

* **package:** return back required exports ([fb7ead6](https://github.com/Romanchuk/angular-i18next/commit/fb7ead6))



<a name="0.2.0"></a>
# [0.2.0](https://github.com/Romanchuk/angular-i18next/compare/0.1.0...0.2.0) (2017-06-28)


### Features

* **package:** AOT support added ([fc1f66d](https://github.com/Romanchuk/angular-i18next/commit/fc1f66d))





================================================
FILE: FUNDING.yml
================================================
github: Romanchuk


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2017 Sergey Romanchuk

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
[![npm version](https://badge.fury.io/js/angular-i18next.svg)](https://badge.fury.io/js/angular-i18next)
[![Downloads](http://img.shields.io/npm/dm/angular-i18next.svg)](https://npmjs.org/package/angular-i18next)
[![Build Status](https://github.com/romanchuk/angular-i18next/actions/workflows/static.yml/badge.svg)](https://github.com/Romanchuk/angular-i18next/actions/workflows/static.yml)
[![Coverage Status](https://coveralls.io/repos/github/Romanchuk/angular-i18next/badge.svg?branch=master)](https://coveralls.io/github/Romanchuk/angular-i18next?branch=master)
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
[![paypal](https://img.shields.io/badge/say_thanks-%2410-green)](https://www.paypal.com/paypalme2/sergeyromanchuk/10USD)
[![GitHub stars](https://img.shields.io/github/stars/romanchuk/angular-i18next?label=Please%20star%20repo%21&style=social)](https://github.com/romanchuk/angular-i18next)

# angular-i18next

Best [i18next](http://i18next.com/) integration with [angular](https://angular.io/)

[Live DEMO](https://angular-i18next.onrender.com)

- [Features](#features)
- [Installation](#installation)
- [Usage](#usage)
- [Cookbook](#cookbook)
- [In-project testing](#in-project-testing)
- [Demo](#demo)
- [Articles](#articles)
- [Support project](#cheers)
- [DEPRECATED DOCS](./README_DEPRECATED.md)

# Features

- Native i18next [options](https://www.i18next.com/configuration-options.html)
- Promise initialization
- [i18next plugin](https://www.i18next.com/plugins-and-utils.html#plugins) support
- Events support
- Namespaces lazy load
- i18next native [format](https://www.i18next.com/api.html#format) support
- document.title localization
- Error handling strategies
- i18next namespaces and scopes (prefixes) for angular modules and components
- AOT support
- SSR support
- Providers for unit testing
- Angular Package Format support
- Zoneless compatible

# Cheers

Star this project

Hey dude! Help me out for a couple of :beers:!

Поддержи проект - угости автора кружечкой пива!

[![paypal](https://img.shields.io/badge/paypal-%2410-green)](https://www.paypal.com/paypalme2/sergeyromanchuk/10USD)

## Available Submodules (optional)

- **`angular-i18next/ssr`**: Adds Server Side Rendering support.
- **`angular-i18next/forms`**: Provides localization for `@angular/forms`.
- **`angular-i18next/testing`**: Offers features for testing.

# Installation

**1.** Install package

   ```bash
    npm install i18next angular-i18next
  ```

**2.** Initialize i18next before angular application and provide

Angular would not load until i18next initialize event fired

```typescript
import { I18NEXT_SERVICE } from 'angular-i18next';

export function i18nAppInit() {
  return () {
    const i18next = inject(I18NEXT_SERVICE);
    return i18next.init();
  }
}
```

```typescript
  providers: [
    provideAppInitializer(i18nAppInit()),
    provideI18Next(
      withCustomErrorHandlingStrategy(StrictErrorHandlingStrategy)
    )
  ] 
```

# Usage

## Pipes

Use "i18next" pipe to translate key:

```html
  <div>{{ 'test' | i18next }}</div>
```

Passing ["t options"](https://www.i18next.com/api.html#t):

```html
    <div>{{ 'test' | i18next: { count: 5, nsSeparator: '#' } }}</div>
```

Remember to import the Pipe into the Component:

```typescript
  @Component({
    // ...
    imports: [I18NextPipe],
  })
  export class SomeExampleComponent {}
```

Trigger native i18next [format method](https://www.i18next.com/formatting.html) by using I18NextFormatPipe or I18NextPipe with option 'format':

`{{ 'any_key' | i18next | i18nextFormat }}`

`{{ 'any_key' | i18next: { format: 'cap' } }}`

`{{ 'any_key' | i18nextCap }}`

**Note:** Using "i18nextCap" you will get the same result as  `i18next: { format: 'cap' }`

**REMEMBER** that format will not work until you set "interpolation.format" function in i18next options.

`angular-i81next` has static method `static interpolationFormat(customFormat: Function = null): Function` that can be used as default interpolation format function (it provides 'upper', 'cap' and 'lower' formatters). You also can pass your custom function to be called after library formatters:

```typescript
import { defaultInterpolationFormat, interpolationFormat } from "angular-i18next";


const i18nextOptions = {
  supportedLngs: ['en', 'ru'],
  ns: [
    'translation',
    'validation',
    'error',
  ],
  interpolation: {
    format: interpolationFormat((value, format, lng) => {
      // extend interpolation format
      if(value instanceof Date)
        return moment(value).format(format);
      return value;
    });
    // format: interpolationFormat(defaultInterpolationFormat)
  }
};

```

**i18nextEager pipe**

This is the impure analog of *i18next pipe* that is subscribed to language change, it will change string right away to choosen language (without reloading page).

**Warning!**: Use i18nextEager only in combine with [OnPush change detection strategy](https://netbasal.com/a-comprehensive-guide-to-angular-onpush-change-detection-strategy-5bac493074a4), or else (default change detection) each pipe will retrigger more than one time (cause of performance issues).

Subscribing to event observables:

```typescript
this.i18NextService.events.languageChanged.subscribe(lang => {
  // do something
})
```

Add a provider to module/component if you want to prefix child i18next keys:

```typescript
{
  provide: I18NEXT_NAMESPACE,
  useValue: 'feature' // set 'feature:' prefix 
}
```

```typescript
{
  provide: I18NEXT_SCOPE,
  useValue: 'person' // set 'person.' prefix 
}
```

Since v3.1.0+ it is possible to pass array of namespaces (or scopes). [Key would fallback](https://www.i18next.com/api.html#t) to next namespace in array if the previous failed to resolve.

`[feature_validators:key, validators:key]`

```typescript
{
  provide: I18NEXT_NAMESPACE,
  useValue: ['feature_validators', 'validators']
}
```

_NOTE:* **Do NOT** use default (or custom) i18next delimiters in namespace names.

### Document title

If you want to turn on document title localization resolve Title as `I18NextTitle` imported from 'angular-i18next':

```typescript
  providers: [provideI18Next(withTitle())]
```

Routes example:

```typescript
const appRoutes: Routes = [
  { 
    path: 'error',
    component: AppErrorComponent,
    data: { title: 'error:error_occured' }
  },
  { 
    path: 'denied',
    component: AccessDeniedComponent,
    data: { title: 'error:access_denied' }
  }
];
```

Ways to use I18NextService in your code:
> **Warning:** Injection of **I18NextService** is possible, but it would not consider I18NEXT_NAMESPACE and I18NEXT_SCOPE providers. There are 2 possible reasons to inject **I18NextService**: initialization and subscription to its events. In all other cases inject **I18NextPipe**.

1) **Recommended way:** Inject via **I18NEXT_SERVICE** token. By default it will inject instance of **I18NextService**.

```typescript
export class AppComponent implements OnInit  {
  constructor(private router: Router,
              private title: Title,
              @Inject(I18NEXT_SERVICE) private i18NextService: ITranslationService) 
```

2) Legacy way: Inject via type

```typescript
export class AppComponent implements OnInit  {
  constructor(private router: Router,
              private title: Title,
              private i18NextService: I18NextService) 
```

### Error handling

Error handling is now configurable:

  1) By default i18next promise will use NativeErrorHandlingStrategy. I18Next would be always resolve successfully. Error could be get from 'then' handler parameter.
  2) Set StrictErrorHandlingStrategy to reject load promises (init, languageChange, loadNamespaces) on first load fail (this was default in v2 but changed to fit [native i18next behavior](https://github.com/Romanchuk/angular-i18next/issues/9):

```typescript
  providers: [
    provideI18Next(
      withCustomErrorHandlingStrategy(StrictErrorHandlingStrategy)
    )
  ]    
```

### Lazy loading

Use `i18NextNamespacesGuard` in your routes to to load i18next namespace.

Note: It is not necessary to register lazy loading namespaces in global i18next options.

```
{
    path: 'rich_form',
    loadComponent: () => RichFormComponent,
    providers: [
      {
          provide: I18NEXT_NAMESPACE, // namespace to start in component
          useValue: 'feature.rich_form',
      },
    ],
    canActivate: [i18NextNamespacesGuard('feature.rich_form')]
 }
```

Use I18NextService.loadNamespaces() method to load namespaces in code.

# Cookbook

### i18next plugin support

```typescript
import { I18NextModule, ITranslationService, I18NEXT_SERVICE } from 'angular-i18next';
//  import Backend from 'i18next-xhr-backend'; //for i18next < 20.0.0
import HttpApi from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';

...

i18next.use(HttpApi)
       .use(LanguageDetector)
       .init(i18nextOptions)
```

### Server side rendereng (SSR)

1. Provide for server:

```typescript
import { provideI18Next, withTitle } from 'angular-i18next';
import { withSSR } from 'angular-i18next/ssr';

const serverConfig: ApplicationConfig = {
  providers: [
    provideServerRendering(),
    provideServerRouting(serverRoutes),
    provideI18Next(withTitle(), withSSR()),
  ],
};

export const config = mergeApplicationConfig(appConfig, serverConfig);
```

2. Configure i18next in `server.ts` ([Example](./apps/angular-i18next-demo/src/server.ts)):

### Auto error message for `@angular/forms`

Use `i18nextValidationMessage` directive with formControlName

```typescript
import { I18NextValidationMessageDirective } from 'angular-i18next/forms'

@Component({
  imports: [I18NextValidationMessageDirective]
})

<input i18nextValidationMessage class="form-control" type="text" formControlName="lastName"/>
```

There is priority order for validation messages:

1. namespace + `control_specific` with form hierarchy
2. namespace +  Common validation key(like `required`)
3. `control_specific` with form hierarchy
4. Common validation key like `required`

Also you can interpolate `control.error` values. For example: For validator `Validators.min(1)`

```json
"min": "Minimal {{min}}. Actual: {{actual}}."
```

`en.validation.json`

```json
{
    "required": "Field is required.",
    "pattern": "$t(validation:_fill) valid value.",
    "_fill": "Please fill in",
    "control_specific": {
        "technicalContact": {
            "firstName": {
                "required": "$t(validation:_fill) technical specialist's first name."
            },
            "lastName": {
                "required": "$t(validation:_fill) technical specialist's last name."
            },
            "middleName": {
                "required": "$t(validation:_fill) technical specialist's patronymic."
            }
        }
    }
}
```

### Testing

```typescript
  import { withSSR } from 'angular-i18next/testing';

  TestBed.configureTestingModule({
      imports: [ProjectComponent],
      providers: [
        provideI18NextMockAppInitializer(),
        provideI18Next(withMock())
      ]
  });
```

# What to do if... ?

## New angular version released, but angular-i18next is not released YET

Angular releases mostly don't break angular-i18next, but we cannot tell ahead that current version of `angular-i18next` will work correctly with latest angular version.

You can override an angular-i18next `peerDependencies` in your `package.json` on your **own risk**:

```json
"overrides": {
  "angular-i18next": {
    "@angular/common": "*",
    "@angular/core": "*",
    "@angular/platform-browser": "*"
  }
}
```

# In-project testing

You might want to unit-test project components that are using i18next pipes

Example tests setup:
[libs/angular-i18next/src/tests/projectTests](https://github.com/Romanchuk/angular-i18next/tree/master/libs/angular-i18next/src/tests/projectTests)

# Demo

[Live DEMO](https://angular-i18next.onrender.com)
Demo app source code available here: <https://github.com/Romanchuk/angular-i18next-demo>

# Articles

- [Angular L10n with I18next](https://phrase.com/blog/posts/angular-l10n-with-i18next/)
- [Best Libraries for Angular I18n](https://phrase.com/blog/posts/best-libraries-for-angular-i18n/)


================================================
FILE: README_DEPRECATED.md
================================================
[![npm version](https://badge.fury.io/js/angular-i18next.svg)](https://badge.fury.io/js/angular-i18next)
[![Downloads](http://img.shields.io/npm/dm/angular-i18next.svg)](https://npmjs.org/package/angular-i18next)
[![Build Status](https://travis-ci.com/Romanchuk/angular-i18next.svg?branch=master)](https://travis-ci.com/Romanchuk/angular-i18next)
[![Coverage Status](https://coveralls.io/repos/github/Romanchuk/angular-i18next/badge.svg?branch=master)](https://coveralls.io/github/Romanchuk/angular-i18next?branch=master)
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
[![paypal](https://img.shields.io/badge/say_thanks-%2410-green)](https://www.paypal.com/paypalme2/sergeyromanchuk/10USD)
[![GitHub stars](https://img.shields.io/github/stars/romanchuk/angular-i18next?label=Please%20star%20repo%21&style=social)](https://github.com/romanchuk/angular-i18next)

# angular-i18next
[i18next](http://i18next.com/) v8.4+ integration with [angular](https://angular.io/) v2.0+

[Live DEMO](https://romanchuk.github.io/angular-i18next/)

 - [Features](#features)
 - [Installation](#installation)
 - [Usage](#usage)
 - [Cookbook](#cookbook)
 - [Deep integration](#deep-integration)
 - [In-project testing](#in-project-testing)
 - [Demo](#demo)
 - [Articles](#articles)
 - [Support project](#support-on-beerpay)
 

# Features

- Native i18next [options](https://www.i18next.com/configuration-options.html)
- Promise initialization
- [i18next plugin](https://www.i18next.com/plugins-and-utils.html#plugins) support 
- Events support
- Namespaces lazy load
- i18next native [format](https://www.i18next.com/api.html#format) support
- document.title localization
- Error handling strategies
- i18next namespaces and scopes (prefixes) for angular modules and components
- AOT support
- [Angular Package Format](https://docs.google.com/document/d/1CZC2rcpxffTDfRDs6p1cfbmKNLA6x5O-NtkJglDaBVs/preview) support

[Related packages](#deep-integration) also has implementations for:
- Reactive forms validators localization
- Http error message localizer

# Cheers!
Hey dude! Help me out for a couple of :beers:!

Поддержи проект - угости автора кружечкой пива!

[![paypal](https://img.shields.io/badge/paypal-%2410-green)](https://www.paypal.com/paypalme2/sergeyromanchuk/10USD)


# Installation

**1.** Install package

   ```
    npm install i18next --save
    npm install angular-i18next --save
  ```

**2.** Import I18NextModule to AppModule

```typescript

import { I18NextModule } from 'angular-i18next';

@NgModule({
  bootstrap: [ AppComponent ],
  declarations: [   
    AppComponent
  ],
  import: [
    I18NextModule.forRoot()
  ]
})
export class AppModule {}

```
**3.** Import I18NextModule.forRoot() to AppModule and setup provider with "init" method (use native [options](https://www.i18next.com/overview/configuration-options)). Angular would not load until i18next initialize event fired
> **Warning:**: options in example valid for i18next v20 (Always check latest API options of i18next)

```typescript
export function appInit(i18next: ITranslationService) {
    return () => i18next.init({
        supportedLngs: ['en', 'ru'],
        fallbackLng: 'en',
        debug: true,
        returnEmptyString: false,
        ns: [
          'translation',
          'validation',
          'error'          
        ],
      });
}

export function localeIdFactory(i18next: ITranslationService)  {
    return i18next.language;
}

export const I18N_PROVIDERS = [
{
    provide: APP_INITIALIZER,
    useFactory: appInit,
    deps: [I18NEXT_SERVICE],
    multi: true
},
{
    provide: LOCALE_ID,
    deps: [I18NEXT_SERVICE],
    useFactory: localeIdFactory
}];
```

```typescript
@NgModule({
    imports: [
        ...
        I18NextModule.forRoot()
    ],
    providers: [
        ...
        I18N_PROVIDERS, 
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}
```

# Usage

### Pipes

Use "i18next" pipe to translate key:

    <div>{{ 'test' | i18next }}</div>

Passing ["t options"](https://www.i18next.com/api.html#t):

    <div>{{ 'test' | i18next: { count: 5, nsSeparator: '#' } }}</div>


Trigger native i18next [format method](https://www.i18next.com/formatting.html) by using I18NextFormatPipe or I18NextPipe with option 'format':

`{{ 'any_key' | i18next | i18nextFormat }}`

`{{ 'any_key' | i18next: { format: 'cap' } }}`

`{{ 'any_key' | i18nextCap }}`

**Note:** Using "i18nextCap" you will get the same result as  `i18next: { format: 'cap' }`

**REMEMBER** that format will not work until you set "interpolation.format" function in i18next options.

I18NextModule has static method `static interpolationFormat(customFormat: Function = null): Function` that can be used as default interpolation format function (it provides 'upper', 'cap' and 'lower' formatters). You also can pass your custom function to be called after I18NextModule formatters:

```typescript
const i18nextOptions = {
  supportedLngs: ['en', 'ru'],
  ns: [
    'translation',
    'validation',
    'error',
  ],
  interpolation: {
    format: I18NextModule.interpolationFormat((value, format, lng) => {
      if(value instanceof Date)
        return moment(value).format(format);
      return value;
    });
    // format: I18NextModule.interpolationFormat()
  }
};

```

**i18nextEager pipe**

This is the impure analog of *i18next pipe* that is subscribed to language change, it will change string right away to choosen language (without reloading page).

**Warning!**: Use i18nextEager only in combine with [OnPush change detection strategy](https://netbasal.com/a-comprehensive-guide-to-angular-onpush-change-detection-strategy-5bac493074a4), or else (default change detection) each pipe will retrigger more than one time (cause of performance issues).

Subscribing to event observables:
```typescript
this.i18NextService.events.languageChanged.subscribe(lang => {
  // do something
})
```

Add a provider to module/component if you want to prefix child i18next keys:
```typescript
{
  provide: I18NEXT_NAMESPACE,
  useValue: 'feature' // set 'feature:' prefix 
}
```
```typescript
{
  provide: I18NEXT_SCOPE,
  useValue: 'person' // set 'person.' prefix 
}
```
Since v3.1.0+ it is possible to pass array of namespaces (or scopes). [Key would fallback](https://www.i18next.com/api.html#t) to next namespace in array if the previous failed to resolve.

`[feature_validators:key, validators:key]`
```typescript
{
  provide: I18NEXT_NAMESPACE,
  useValue: ['feature_validators', 'validators']
}
```
_NOTE:_ **Do NOT** use default (or custom) i18next delimiters in namespace names.

### Document title
If you want to turn on document title localization resolve Title as `I18NextTitle` imported from 'angular-i18next':

```typescript
{
  provide: Title,
  useClass: I18NextTitle
}
```

Also you can implement your own Title service with specific behavior. Inject `I18NextPipe` (or `I18NextService`) to service/component:
```typescript
import { Injectable, Inject } from '@angular/core';
import { Title, DOCUMENT } from '@angular/platform-browser';
import { I18NextPipe } from 'angular-i18next';

@Injectable()
export class I18NextTitle extends Title {
   constructor(private i18nextPipe: I18NextPipe, @Inject(DOCUMENT) doc) {
    super(doc);
   }

   setTitle(value: string) {
    return super.setTitle(this.translate(value));
   }

   private translate(text: string) {
     return this.i18nextPipe.transform(text, { format: 'cap'});
   }
}

```

Ways to use I18NextService in your code:
> **Warning:** Injection of **I18NextService** is possible, but it would not consider I18NEXT_NAMESPACE and I18NEXT_SCOPE providers. There are 2 possible reasons to inject **I18NextService**: initialization and subscription to its events. In all other cases inject **I18NextPipe**.
1) **Recommended way:** Inject via **I18NEXT_SERVICE** token. By default it will inject instance of **I18NextService**.
```typescript
export class AppComponent implements OnInit  {
  constructor(private router: Router,
              private title: Title,
              @Inject(I18NEXT_SERVICE) private i18NextService: ITranslationService) 
```

2) Legacy way: Inject via type
```typescript
export class AppComponent implements OnInit  {
  constructor(private router: Router,
              private title: Title,
              private i18NextService: I18NextService) 
```

### Error handling

Error handling is now configurable:
  1) By default i18next promise will use NativeErrorHandlingStrategy. I18Next would be always resolve succesfully. Error could be get from 'then' handler parameter.
  2) Set StrictErrorHandlingStrategy to reject load promises (init, languageChange, loadNamespaces) on first load fail (this was default in v2 but changed to fit [native i18next behavior](https://github.com/Romanchuk/angular-i18next/issues/9):

    `I18NextModule.forRoot({ errorHandlingStrategy: StrictErrorHandlingStrategy })`

    

### Lazy loading

Use I18NEXT_NAMESPACE_RESOLVER in your routes to to load i18next namespace.

Note: It is not neccesary to register lazy loading namespaces in global i18next options.

```
{
    path: 'rich_form',
    loadChildren: 'app/features/rich_form_feature/RichFormFeatureModule#RichFormFeatureModule',
    data: {
      i18nextNamespaces: ['feature.rich_form']
    },
    resolve: {
      i18next: I18NEXT_NAMESPACE_RESOLVER
    }
 },

```
Use I18NextService.loadNamespaces() method to load namespaces in code.


# Cookbook

### i18next plugin support

```typescript
import { I18NextModule, ITranslationService, I18NEXT_SERVICE } from 'angular-i18next';
//  import Backend from 'i18next-xhr-backend'; //for i18next < 20.0.0
import HttpApi from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';

...

i18next.use(HttpApi)
       .use(LanguageDetector)
       .init(i18nextOptions)
```




### Initialize i18next before angular application
Angular would not load until i18next initialize event fired
```typescript
export function appInit(i18next: ITranslationService) {
    return () => i18next.init();
}

export function localeIdFactory(i18next: ITranslationService)  {
    return i18next.language;
}

export const I18N_PROVIDERS = [
{
    provide: APP_INITIALIZER,
    useFactory: appInit,
    deps: [I18NEXT_SERVICE],
    multi: true
},
{
    provide: LOCALE_ID,
    deps: [I18NEXT_SERVICE],
    useFactory: localeIdFactory
}];
```



### Document title update on language or route change


```typescript
export class AppComponent implements OnInit  {
  constructor(private router: Router,
              private title: Title,
              @Inject(I18NEXT_SERVICE) private i18NextService: ITranslationService) {
      // page title subscription
      // https://toddmotto.com/dynamic-page-titles-angular-2-router-events#final-code
      this.router.events
        .filter(event => event instanceof NavigationEnd)
        .map(() => this.router.routerState.root)
        .map(route => {
          while (route.firstChild) route = route.firstChild;
          return route;
        })
        .filter(route => route.outlet === 'primary')
        .mergeMap(route => route.data)
        .subscribe((event) => this.updatePageTitle(event['title']));
  }

  ngOnInit() {
    this.i18NextService.events.languageChanged.subscribe(lang => {
      let root = this.router.routerState.root;
      if (root != null && root.firstChild != null) {
        let data: any = root.firstChild.data;
        this.updatePageTitle(data && data.value && data.value.title);
      }
    });
  }

  updatePageTitle(title: string): void {
    let newTitle = title || 'application_title';
    this.title.setTitle(newTitle);
  }
}
```
Routes example:
```typescript
const appRoutes: Routes = [
  { 
    path: 'error',
    component: AppErrorComponent,
    data: { title: 'error:error_occured' }
  },
  { 
    path: 'denied',
    component: AccessDeniedComponent,
    data: { title: 'error:access_denied' }
  }
];
```

# What to do if... ?

## New angular version released, but angular-i18next is not released YET!!!

Angular releases mostly don't break angular-i18next, but we cannot tell ahead that current version of `angular-i18next` will work correctly with latest angular version.

You can override an angular-i18next `peerDependencies` in your `package.json` on your **own risk**:

```json
"overrides": {
  "angular-i18next": {
    "@angular/common": "*",
    "@angular/core": "*",
    "@angular/platform-browser": "*"
  }
}
```

# Deep integration

List of packages to integrate angular and i18next more deeply:

- [angular-validation-message](https://github.com/Romanchuk/angular-validation-message) - angular [reactive form validators](https://angular.io/guide/reactive-forms#step-2-making-a-field-required) integration (and [angular-validation-message-i18next ](https://github.com/Romanchuk/angular-validation-message-i18next) is i18next bridge to it). It gives you possibility to localize form validators and it automatically puts localized validator error message to markup (if there is one).
- [angular-i18next-error-interceptor](https://github.com/LCGroupIT/angular-i18next-error-interceptor) - allows you to set default errot messages for non-200 http status responses. So if the back-end didn't specify { message: 'some error' } in a response (sort of contract with our backend) interceptor will check response status code and will fill { message: 'Server is not available. Please try again.' }. Also package includes pipe where you can pass HttpErrorResponse and it will return error message whenever it's back-end message or our localized message.

# In-project testing

You might want to unit-test project components that are using i18next pipes

Example tests setup:
[libs/angular-i18next/src/tests/projectTests](https://github.com/Romanchuk/angular-i18next/tree/master/libs/angular-i18next/src/tests/projectTests)

# Demo

[Live DEMO](https://romanchuk.github.io/angular-i18next-demo/)
Demo app source code available here: https://github.com/Romanchuk/angular-i18next-demo


# Articles
- [Angular L10n with I18next](https://phrase.com/blog/posts/angular-l10n-with-i18next/)
- [Best Libraries for Angular I18n](https://phrase.com/blog/posts/best-libraries-for-angular-i18n/)



================================================
FILE: apps/angular-i18next-demo/.browserslistrc
================================================
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries

# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support

# You can see what browsers were selected by your queries by running:
#   npx browserslist

last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR


================================================
FILE: apps/angular-i18next-demo/.eslintrc.json
================================================
{
  "extends": ["../../.eslintrc.json"],
  "ignorePatterns": ["!**/*"],
  "overrides": [
    {
      "files": ["*.ts"],
      "extends": [
        "plugin:@nx/angular",
        "plugin:@angular-eslint/template/process-inline-templates"
      ],
      "rules": {}
    },
    {
      "files": ["*.html"],
      "extends": ["plugin:@nx/angular-template"],
      "rules": {}
    }
  ]
}


================================================
FILE: apps/angular-i18next-demo/jest.config.ts
================================================
/* eslint-disable */
export default {
  displayName: 'angular-i18next-demo',
  preset: '../../jest.preset.js',
  setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
  globals: {},
  coverageDirectory: '../../coverage/apps/angular-i18next-demo',
  transform: {
    '^.+\\.(ts|mjs|js|html)$': [
      'jest-preset-angular',
      {
        tsconfig: '<rootDir>/tsconfig.spec.json',
        stringifyContentPathRegex: '\\.(html|svg)$',
      },
    ],
  },
  transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
  snapshotSerializers: [
    'jest-preset-angular/build/serializers/no-ng-attributes',
    'jest-preset-angular/build/serializers/ng-snapshot',
    'jest-preset-angular/build/serializers/html-comment',
  ],
};


================================================
FILE: apps/angular-i18next-demo/project.json
================================================
{
  "name": "angular-i18next-demo",
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "projectType": "application",
  "sourceRoot": "apps/angular-i18next-demo/src",
  "prefix": "angular-i18next",
  "tags": [],
  "targets": {
    "build": {
      "executor": "@nx/angular:application",
      "outputs": ["{options.outputPath}"],
      "options": {
        "browser": "apps/angular-i18next-demo/src/main.ts",
        "outputPath": "dist/angular-i18next-demo",
        "index": "apps/angular-i18next-demo/src/index.html",
        "tsConfig": "apps/angular-i18next-demo/tsconfig.app.json",
        "assets": [
          "apps/angular-i18next-demo/src/assets/favicon.png",
          "apps/angular-i18next-demo/src/assets",
          "apps/angular-i18next-demo/src/locales"
        ],
        "styles": ["apps/angular-i18next-demo/src/styles.css"],
        "scripts": [],
        "server": "apps/angular-i18next-demo/src/main.server.ts",
        "ssr": {
          "entry": "apps/angular-i18next-demo/src/server.ts"
        },
        "outputMode": "server"
      },
      "configurations": {
        "production": {
          "budgets": [
            {
              "type": "initial",
              "maximumWarning": "500kb",
              "maximumError": "1mb"
            },
            {
              "type": "anyComponentStyle",
              "maximumWarning": "2kb",
              "maximumError": "4kb"
            }
          ],
          "fileReplacements": [
            {
              "replace": "apps/angular-i18next-demo/src/environments/environment.ts",
              "with": "apps/angular-i18next-demo/src/environments/environment.prod.ts"
            }
          ],
          "outputHashing": "all"
        },
        "development": {
          "optimization": false,
          "extractLicenses": false,
          "sourceMap": true,
          "namedChunks": true
        }
      },
      "defaultConfiguration": "production"
    },
    "serve": {
      "executor": "@nx/angular:dev-server",
      "configurations": {
        "production": {
          "buildTarget": "angular-i18next-demo:build:production"
        },
        "development": {
          "buildTarget": "angular-i18next-demo:build:development"
        }
      },
      "defaultConfiguration": "development"
    },
    "extract-i18n": {
      "executor": "@angular-devkit/build-angular:extract-i18n",
      "options": {
        "buildTarget": "angular-i18next-demo:build"
      }
    },
    "lint": {
      "executor": "@nx/eslint:lint",
      "options": {
        "lintFilePatterns": [
          "apps/angular-i18next-demo/**/*.ts",
          "apps/angular-i18next-demo/**/*.html"
        ]
      }
    },
    "test": {
      "executor": "@nx/jest:jest",
      "outputs": ["{workspaceRoot}/coverage/apps/angular-i18next-demo"],
      "options": {
        "jestConfig": "apps/angular-i18next-demo/jest.config.ts",
        "passWithNoTests": true
      }
    }
  }
}


================================================
FILE: apps/angular-i18next-demo/src/app/app.component.css
================================================


================================================
FILE: apps/angular-i18next-demo/src/app/app.component.html
================================================
<div class="app-wrapper">
  @if (loading) {
  <div class="app-loader">
      <div class="loader-pusher">
          <div class="spinner">
              <div class="spinner-tail"></div>
          </div>
      </div>
  </div>
  }
  <app-header></app-header>
  <main>
      <div class="app-body">
          <div class="container">
              <hr />
              <p class="lead">{{ 'intro' | i18next }}</p>
              <hr />
              <router-outlet></router-outlet>
          </div>
      </div>
  </main>
  <app-footer></app-footer>
</div>


================================================
FILE: apps/angular-i18next-demo/src/app/app.component.ts
================================================

import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
import { Title } from '@angular/platform-browser';
import {
  Event as RouterEvent, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router,
  RouterOutlet
} from '@angular/router';
import { I18NEXT_SERVICE, I18NextPipe, ITranslationService } from 'angular-i18next';
import { filter, map, mergeMap, tap } from 'rxjs/operators';
import { AppHeaderComponent } from "./structure/app-header.component";
import { AppFooterComponent } from "./structure/app-footer.component";


@Component({
  selector: 'app',
  encapsulation: ViewEncapsulation.None,
  templateUrl: './app.component.html',
  standalone: true,
  imports: [I18NextPipe, AppHeaderComponent, AppFooterComponent, RouterOutlet],
})
export class AppComponent implements OnInit  {

  loading = true;
  start = 0;

  get title() {
    return this._title.getTitle();
  }

  constructor(private router: Router,
              private _title: Title,
              @Inject(I18NEXT_SERVICE) private i18NextService: ITranslationService) {


      // spinner/loader subscription
      router.events
        .subscribe((event: RouterEvent) => {
            this.navigationInterceptor(event);
        });
      // page title subscription
      // https://toddmotto.com/dynamic-page-titles-angular-2-router-events#final-code
      this.router.events
        .pipe(
            filter(event => event instanceof NavigationEnd),
            map(() => this.router.routerState.root),
            map(route => {
              while (route.firstChild) route = route.firstChild;
              return route;
            }),
            filter(route => route.outlet === 'primary'),
            mergeMap(route => route.data)
        )
        .subscribe((event) => this.updatePageTitle(event['title']));
  }

  ngOnInit() {
    this.i18NextService.events.languageChanged.subscribe(() => {
      const root = this.router.routerState.root;
      if (root != null && root.firstChild != null) {
        const data = root.firstChild.data;
        data
          .pipe(
            tap((data) => {
              this.updatePageTitle(data && data['value'] && data['value'].title);
            })
          )
          .subscribe();

      }
    });
  }

  // http://stackoverflow.com/questions/37069609/show-loading-screen-when-navigating-between-routes-in-angular-2
  navigationInterceptor(event: RouterEvent): void {
        if (event instanceof NavigationStart) {
            this.loading = true;
        }
        if (event instanceof NavigationEnd
            || event instanceof NavigationCancel
            || event instanceof NavigationError) {
            this.loading = false;
        }
    }

    updatePageTitle(title: string): void {
      const newTitle = title || 'application_title';
      console.log('Setting page title:', newTitle);
      this._title.setTitle(newTitle);
      console.log('Setting page title end:', newTitle);
    }
}


================================================
FILE: apps/angular-i18next-demo/src/app/app.config.server.ts
================================================
import { ApplicationConfig, mergeApplicationConfig } from '@angular/core';
import { provideServerRendering, withRoutes } from '@angular/ssr';
import { provideI18Next, withTitle } from 'angular-i18next';
import { withSSR } from 'angular-i18next/ssr';
import { appConfig } from './app.config';
import { serverRoutes } from './app.routes.server';

const serverConfig: ApplicationConfig = {
  providers: [
    provideServerRendering(withRoutes(serverRoutes)),
    provideI18Next(withTitle(), withSSR()),
  ],
};

export const config = mergeApplicationConfig(appConfig, serverConfig);


================================================
FILE: apps/angular-i18next-demo/src/app/app.config.ts
================================================
import { isPlatformBrowser } from '@angular/common';
import {
  ApplicationConfig,
  importProvidersFrom,
  inject,
  PLATFORM_ID,
  provideAppInitializer,
  provideZonelessChangeDetection,
} from '@angular/core';
import { FormsModule } from '@angular/forms';
import {
  BrowserModule,
  provideClientHydration,
  withEventReplay,
} from '@angular/platform-browser';
import { I18NEXT_SERVICE, I18NextLoadResult, provideI18Next, withTitle } from 'angular-i18next';
import HttpApi from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import i18nextOptions from './i18next.options';
import { provideRouter } from '@angular/router';
import { appRoutes } from './app.routes';

export function appInit() {
  return () => {
    const i18next = inject(I18NEXT_SERVICE);
    const platformId = inject(PLATFORM_ID);

    if (!isPlatformBrowser(platformId)) {
      return Promise.resolve();
    }
    const promise: Promise<I18NextLoadResult> = i18next
      .use(HttpApi)
      .use(LanguageDetector)
      .init(i18nextOptions);
    return promise;
  };
}

export const appConfig: ApplicationConfig = {
  providers: [
    provideClientHydration(withEventReplay()),
    provideZonelessChangeDetection(),
    provideRouter(appRoutes),
    importProvidersFrom(BrowserModule, FormsModule),
    provideAppInitializer(appInit()),
    provideI18Next(withTitle()),
  ],
};


================================================
FILE: apps/angular-i18next-demo/src/app/app.routes.server.ts
================================================
import { RenderMode, ServerRoute } from '@angular/ssr';

export const serverRoutes: ServerRoute[] = [
  {
    path: '**',
    renderMode: RenderMode.Server,
  },
];


================================================
FILE: apps/angular-i18next-demo/src/app/app.routes.ts
================================================
import { Routes } from "@angular/router";
import { I18NEXT_NAMESPACE, i18NextNamespacesGuard } from "angular-i18next";
import { AccessDeniedComponent } from "./content/access-denied/access-denied.component";
import { SimpleDemoComponent } from "./content/simple-demo.component";
import { RichFormComponent } from "./features/rich_form_feature/rich-form.component";

export const appRoutes: Routes = [
  { path: '', component: SimpleDemoComponent },
  {
    path: 'rich_form',
    loadComponent: () => RichFormComponent,
    data: {
      title: 'feature.rich_form:title'
    },
    providers: [
      {
          provide: I18NEXT_NAMESPACE,
          useValue: 'feature.rich_form',
      },
    ],
    canActivate: [i18NextNamespacesGuard('feature.rich_form')]
 },
  { path: 'denied', component: AccessDeniedComponent, data: { title: 'error:access_denied' }}
];


================================================
FILE: apps/angular-i18next-demo/src/app/content/access-denied/access-denied.component.html
================================================
<div class="main-header">
    <h2>{{ 'error:access_denied' | i18next: { case: 'cap' } }}</h2>
</div>


================================================
FILE: apps/angular-i18next-demo/src/app/content/access-denied/access-denied.component.ts
================================================

import { Component } from '@angular/core';
import { I18NextPipe } from "angular-i18next";

@Component({
  selector: 'access-denied',
  templateUrl: './access-denied.component.html',
  standalone: true,
  imports: [I18NextPipe]
})
export class AccessDeniedComponent {

}


================================================
FILE: apps/angular-i18next-demo/src/app/content/simple-demo.component.html
================================================
<div class="container">
    <br/>
    <h1>{{ 'simple_demo' | i18nextCap }}</h1>
    <br/>
    <hr/>
    <div class="row">
        <h4>
            {{ 'parametrized_string_title' | i18nextCap }}
        </h4>
        <div class="input-group">
            <div class="form-inline">
                <div class="form-group">
                    <span>{{ 'parametrized_string' | i18next: { value: value, str: str } }}</span>
                </div>
                <div class="form-group mx-sm-3">
                    <input name="number" type="number" class="form-control" [(ngModel)]="value">
                </div>
                <div class="form-group mx-sm-3">
                    <input name="str" type="text" class="form-control" [(ngModel)]="str">
                </div>
            </div>
        </div>
    </div>
    <br/>
    <hr/>
    <div class="row">
        <h4>
            {{ 'case_demo_title' | i18nextCap }}
        </h4>
        <div class="form-inline">
            <table class="table table-hover">
                <thead>
                    <tr>
                        <th>original</th>
                        <th>capitalize</th>
                        <th>lowercase</th>
                        <th>uppercase</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>{{ 'case_demo' | i18next }}</td>
                        <td>{{ 'case_demo' | i18next: { format: 'capitalize' } }}</td>
                        <td>{{ 'case_demo' | i18next: { format: 'lowercase' } }}</td>
                        <td>{{ 'case_demo' | i18next | i18nextFormat: 'uppercase' }}</td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</div>

================================================
FILE: apps/angular-i18next-demo/src/app/content/simple-demo.component.ts
================================================

import { Component, ViewEncapsulation } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { I18NextCapPipe, I18NextFormatPipe, I18NextPipe } from "angular-i18next";

@Component({
  selector: 'simple-demo',
  encapsulation: ViewEncapsulation.None,
  templateUrl: './simple-demo.component.html',
  standalone: true,
  imports: [I18NextCapPipe, I18NextPipe, I18NextFormatPipe, FormsModule]
})
export class SimpleDemoComponent {
  value = 15;
  str = 'Hello';
}


================================================
FILE: apps/angular-i18next-demo/src/app/features/rich_form_feature/rich-form.component.html
================================================
<div>
    <div class="main-header">
        <h1>{{ 'title' | i18next: { case: 'cap' } }}</h1>
    </div>
    <br/>
    <form #requestHtmlForm class="form-default" name="requestHtmlForm" [formGroup]="form" (ngSubmit)="onSubmit($event)">

        <div class="row">
            <div class="col-6">
                <div class="form-group">
                    <label class="control-label control-label-sm text-muted">{{ 'email' | i18nextCap }}</label>
                    <input i18nextValidationMessage name="email" type="email" class="form-control" formControlName="email" />
                </div>
            </div>
            <div class="col-6">
                <div class="form-group">
                    <label class="control-label control-label-sm text-muted">{{ 'count' | i18nextCap }}</label>
                    <input  i18nextValidationMessage name="count" type="number" class="form-control" formControlName="count" />
                </div>
            </div>
        </div>

        <div class="form-group">
            <div>{{ 'technical_contact' | i18nextEager }}</div>
        </div>

        <div formGroupName="technicalContact">

            <div class="row">
                <div class="col-6">
                    <div class="form-group">
                        <label class="control-label control-label-sm text-muted">{{ 'person.last_name' | i18next: { format: 'cap' } }}</label>
                        <input i18nextValidationMessage class="form-control" type="text" formControlName="lastName"/>
                    </div>
                </div>
                <div class="col-6">
                    <div class="form-group">
                        <label class="control-label control-label-sm text-muted">{{ 'person.first_name' | i18next: { format: 'cap' } }}</label>
                        <input i18nextValidationMessage class="form-control" type="text" formControlName="firstName">
                    </div>
                </div>
            </div>

            <div class="row">
                <div class="col-6">
                    <div class="form-group">
                        <label class="control-label control-label-sm text-muted">{{ 'person.middle_name' | i18next: { format: 'cap' } }}</label>
                        <input i18nextValidationMessage class="form-control" type="text" formControlName="middleName">
                    </div>
                </div>
                <div class="col-6">
                </div>
            </div>
        </div>
        <div class="row">
            <div class="col-4">
                <div class="form-group">
                    <button type="submit" class="btn btn-primary btn-block">{{ 'buttons.send' | i18nextCap }}</button>
                </div>
            </div>
        </div>
    </form>
</div>


================================================
FILE: apps/angular-i18next-demo/src/app/features/rich_form_feature/rich-form.component.ts
================================================

import { Component, ViewEncapsulation } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { I18NextCapPipe, I18NextEagerPipe, I18NextPipe } from "angular-i18next";
import { I18NextValidationMessageDirective } from 'angular-i18next/forms';
import { ValidationDirtyChecker } from '../../../lib/validation/services/ValidationDirtyChecker';
import { RichFormModel } from './rich-form.model';

@Component({
  selector: 'rich-form',
  encapsulation: ViewEncapsulation.None,
  templateUrl: './rich-form.component.html',
  standalone: true,
  imports: [I18NextCapPipe, I18NextPipe, I18NextEagerPipe, ReactiveFormsModule, I18NextValidationMessageDirective],
  providers: [ValidationDirtyChecker]
})
export class RichFormComponent {

  form: FormGroup;
  model: RichFormModel = new RichFormModel();

  constructor(private fb: FormBuilder, private readonly validationDirtyChecker: ValidationDirtyChecker) {
    this.form = this.fb.group({
      'count': [this.model.count, [Validators.min(1), Validators.max(3)]],
      'email': [this.model.email, [Validators.email]],
      'technicalContact': this.fb.group({
        'firstName': [this.model.technicalContact.firstName, [Validators.required]],
        'lastName': [this.model.technicalContact.lastName, [Validators.required]],
        'middleName': [this.model.technicalContact.middleName, [Validators.required]],
      })
    });
  }


  onSubmit(e: Event) {
    if (!this.form.valid) {
        this.validationDirtyChecker.markControlsDirty(this.form);
         return;
    }
  }
}


================================================
FILE: apps/angular-i18next-demo/src/app/features/rich_form_feature/rich-form.model.ts
================================================
export class Contact {
    lastName: string | undefined;
    firstName: string | undefined;
    middleName: string | undefined;
}

export class RichFormModel {
    email: string | undefined;
    count = 0;
    technicalContact: Contact = new Contact();
}


================================================
FILE: apps/angular-i18next-demo/src/app/i18next.options.ts
================================================
import { defaultInterpolationFormat, interpolationFormat } from "angular-i18next";
import type * as i18n from 'i18next';
import type { HttpBackendOptions } from "i18next-http-backend";

export const i18nextOptions: i18n.InitOptions & { backend: HttpBackendOptions} = {
  supportedLngs:['en', 'ru'],
  fallbackLng: 'en',
  debug: true,
  returnEmptyString: false,
  ns: [
    'translation',
    'validation',
    'error'
  ],
  interpolation: {
    format: interpolationFormat(defaultInterpolationFormat)
  },
  //backend plugin options
  backend: {
    loadPath: 'locales/{{lng}}.{{ns}}.json',
  },
  // lang detection plugin options
  detection: {
    // order and from where user language should be detected
    order: ['cookie', 'header'],

    // keys or params to lookup language from
    lookupCookie: 'lang',
   // lookupHeader: 'accept-language',
    // cache user language on
    caches: ['cookie'],

    // optional expire and domain for set cookie
    cookieMinutes: 10080, // 7 days
  }
};

export default i18nextOptions;


================================================
FILE: apps/angular-i18next-demo/src/app/structure/app-error.component.html
================================================
<div class="error-page">
    <div class="viewport">
        <div class="page-404">
            <div class="cols">
                <div class="col">
                    <div class="error-code">{{ 'error:oops' | i18next }}</div>
                </div>
                <div class="col">
                    <div class="error-description">{{ 'error:error_occured_onload' | i18next }}</div>
                    <ul>
                        <li>
                            <p>{{ 'error:contact_administrator_or_try_to_clear_browser_chache_and_restart_application' | i18next }}</p>
                            <button class="btn btn-success btn-block" (click)="reload()">{{ 'error:restart' | i18next }}</button>
                            <div class="popover-wrapper">
                                @if (showed) {
                                  <div>
                                    <svg class="icon" (click)="close()">
                                        <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="assets/images/icons.svg#cross"></use>
                                    </svg>
                                    <div class="popover-header">{{ 'error:cookies.how_to' | i18next }}</div>
                                    <div class="popover-body">{{ 'error:cookies.clear_chrome' | i18next }}</div>
                                 </div>
                                }

                                <svg class="icon icon-question-circle-black" placement="right" (click)="toggle()">
                                    <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="assets/images/icons.svg#question-circle-black"></use>
                                </svg>
                            </div>
                        </li>
                        <li>
                            <p>{{ 'error:need_help_write_to_us' | i18next }}</p>
                            <a class="link text-sm" href="#">
                                {{ 'error:write' | i18next }}
                                <svg class="icon icon_link-arrow">
                                    <use xlink:href="assets/images/icons.svg#link-arrow" xmlns:xlink="http://www.w3.org/1999/xlink"></use>
                                </svg>
                            </a>
                        </li>
                    </ul>
                </div>
            </div>
        </div>
    </div>
</div>


================================================
FILE: apps/angular-i18next-demo/src/app/structure/app-error.component.ts
================================================

import { Component, ViewEncapsulation } from '@angular/core';
import { I18NextPipe } from 'angular-i18next';

@Component({
  selector: 'app-error',
  encapsulation: ViewEncapsulation.None,
  templateUrl: './app-error.component.html',
  standalone: true,
  imports: [I18NextPipe]
})
export class AppErrorComponent {
  public showed = false;
  public toggle(){
    this.showed = !this.showed;
  }
  public close(){
    this.showed = false;
  }
  public reload(){
    document.location.href = document.location.protocol + '//' + document.location.host;
  }
}


================================================
FILE: apps/angular-i18next-demo/src/app/structure/app-footer.component.html
================================================
<footer>
    <nav class="fixed-bottom navbar navbar-expand-lg navbar-light" style="background-color: #f2f2f2;">
        <div><a href="https://angular.io" target="_blank">angular</a> v.{{angularVersion}} / <a href="https://www.i18next.com" target="_blank">i18next</a> v.{{i18nextVersion}}</div>


        <iframe src="https://ghbtns.com/github-btn.html?user=romanchuk&repo=angular-i18next&type=star&count=true" frameborder="0" scrolling="0" width="170px" height="20px"></iframe>
    </nav>
</footer>

================================================
FILE: apps/angular-i18next-demo/src/app/structure/app-footer.component.ts
================================================

import { Component, VERSION, ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'app-footer',
  encapsulation: ViewEncapsulation.None,
  templateUrl: './app-footer.component.html',
  standalone: true
})
export class AppFooterComponent {
  angularVersion = '0.0.0';
  i18nextVersion = '0.0.0';
  constructor(){
    this.angularVersion = VERSION.full;
    this.i18nextVersion = '24.2.1';
  }

}


================================================
FILE: apps/angular-i18next-demo/src/app/structure/app-header.component.html
================================================
<header>
    <nav class="navbar navbar-expand-lg navbar-dark" style="background-color: #f2f2f2;">
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <a class="navbar-brand" href="#">
                <img src="assets/logo.svg" width="80" height="40" class="d-inline-block align-top" alt="" />
            </a>
            <b>{{ 'application_title' | i18next }}</b>
            <ul class="nav nav-pills">
                <li class="nav-item">
                    <a class="nav-link" routerLink="/">{{ 'simple_demo' | i18nextCap }}</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" routerLink="/rich_form">{{ 'rich_form_title' | i18nextCap }}</a>
                </li>
            </ul>
        </div>
        <header-language></header-language>
    </nav>
</header>


================================================
FILE: apps/angular-i18next-demo/src/app/structure/app-header.component.ts
================================================
import { Component, ViewEncapsulation } from '@angular/core';
import { I18NextCapPipe, I18NextPipe } from 'angular-i18next';
import { HeaderLanguageComponent } from "./header-controls/header.language.component";
import { RouterLink } from '@angular/router';

@Component({
  selector: 'app-header',
  encapsulation: ViewEncapsulation.None,
  templateUrl: './app-header.component.html',
  standalone: true,
  imports: [I18NextPipe, I18NextCapPipe, HeaderLanguageComponent, RouterLink],
})
export class AppHeaderComponent {}


================================================
FILE: apps/angular-i18next-demo/src/app/structure/header-controls/header.language.component.html
================================================
<div>
  @for (lang of languages; track lang) {
    @if (currentLanguage() !== lang) {
      <a  href="javascript:void(0)" class="link lang-item {{lang}}" (click)="changeLanguage(lang)">{{ '_languages.' + lang | i18nextCap }}</a>
    } @else {
      <span class="current lang-item {{lang}}">{{ '_languages.' + lang | i18nextCap }}</span>
    }
  }
</div>


================================================
FILE: apps/angular-i18next-demo/src/app/structure/header-controls/header.language.component.ts
================================================

import { Component, Inject, ViewEncapsulation, OnInit, signal } from '@angular/core';
import { I18NEXT_SERVICE, I18NextCapPipe, ITranslationService } from 'angular-i18next';

@Component({
  selector: 'header-language',
  encapsulation: ViewEncapsulation.None,
  templateUrl: './header.language.component.html',
  standalone: true,
  imports: [I18NextCapPipe]
})
export class HeaderLanguageComponent implements OnInit {

  currentLanguage = signal('ru');
  languages: string[] = ['ru', 'en'];

  constructor(
    @Inject(I18NEXT_SERVICE) private i18NextService: ITranslationService
  )
  {}

  ngOnInit() {
    this.i18NextService.events.initialized.subscribe((e) => {
      if (e) {
        this.updateState(this.i18NextService.language);
      }
    });
  }

  changeLanguage(lang: string){
    if (lang !== this.i18NextService.language) {
      this.i18NextService.changeLanguage(lang).then(() => {
        this.updateState(lang);
        document.location.reload();
      });
    }
  }

  private updateState(lang: string) {
    this.currentLanguage.set(lang);
  }

}


================================================
FILE: apps/angular-i18next-demo/src/assets/.gitkeep
================================================


================================================
FILE: apps/angular-i18next-demo/src/assets/ng-validation.css
================================================

input.ng-dirty.ng-invalid,
textarea.ng-dirty.ng-invalid,
.form-control.ng-dirty.ng-invalid,
.ng-dirty.ng-invalid:focus
{
    background-color: #FDEDED;
    border-color: #D22630;
    -webkit-box-shadow: none;
    box-shadow: none;
}

/* s7 ui kit fix */
.form-group .error-container {
  display: block;
}

.error-container {
	color: #D22630;
	padding-top: 2px;
}


/* end fix */

checkbox.ng-invalid .custom-control-indicator,
multiplecheckbox.ng-invalid.ng-dirty .custom-control-indicator,
flatpickr.ng-invalid.ng-dirty .form-control, 
datepicker.ng-invalid.ng-dirty .select2-container .select2-selection,
radio-button.ng-dirty.ng-invalid .custom-control-indicator,
div.ng-invalid.ng-dirty.form-group-valid .custom-control-indicator,
div.ng-invalid.ng-dirty.form-group-valid .select2-container .select2-selection {
    border-color: #D22630;
    background-color: #FDEDED;
}

radio-button + radio-button,
checkbox + checkbox {
  margin-left: 15px;
}




================================================
FILE: apps/angular-i18next-demo/src/environments/environment.prod.ts
================================================
export const environment = {
  production: true
};


================================================
FILE: apps/angular-i18next-demo/src/environments/environment.ts
================================================
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.

export const environment = {
  production: false
};


================================================
FILE: apps/angular-i18next-demo/src/index.html
================================================
<!DOCTYPE html>
<html>

  <head>
      <meta charset="utf-8">
      <meta http-equiv="x-ua-compatible" content="ie=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <meta name="description" content="DEMO angular-i18next">

      <title>
          DEMO angular-i18next
      </title>
      <link rel="icon" type="image/ico" href="assets/favicon.png" />
      <!-- base url -->
      <base href="/">

      <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
      <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>

      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">

  </head>

  <body>

      <app>
          <style type="text/css">
              * {
                  outline: 0!important
              }

              .app-loader {
                  display: table;
                  height: 100vh;
                  width: 100%
              }

              .app-loader .loader-pusher {
                  display: table-cell;
                  text-align: center;
                  vertical-align: middle
              }

              .app-loader .loader-pusher .spinner {
                  background: url('assets/logo.svg') no-repeat 50% 50% #f2f2f2;
                  background-size: 80%;
                  border-radius: 50%;
                  display: inline-block;
                  height: 80px;
                  position: relative;
                  width: 80px;
                  line-height: 80px;
                  text-align: center;
                  color: #fff;
              }

              .app-loader .loader-pusher .spinner .spinner-tail {
                  -webkit-animation: spinner .86s linear infinite;
                  animation: spinner .86s linear infinite;
                  background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzgiIGhlaWdodD0iMzgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IHgxPSI4LjA0MiUiIHkxPSIwJSIgeDI9IjY1LjY4MiUiIHkyPSIyMy44NjUlIiBpZD0iYSI+PHN0b3Agc3RvcC1jb2xvcj0iI2JlYmViZSIgc3RvcC1vcGFjaXR5PSIwIiBvZmZzZXQ9IjAlIi8+PHN0b3Agc3RvcC1jb2xvcj0iI2JlYmViZSIgc3RvcC1vcGFjaXR5PSIuNjMxIiBvZmZzZXQ9IjYzLjE0NiUiLz48c3RvcCBzdG9wLWNvbG9yPSIjYmViZWJlIiBvZmZzZXQ9IjEwMCUiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxIDEpIiBmaWxsPSJub25lIj48cGF0aCBkPSJNMzYgMThjMC05Ljk0LTguMDYtMTgtMTgtMTgiIHN0cm9rZT0idXJsKCNhKSIgc3Ryb2tlLXdpZHRoPSIyIi8+PGNpcmNsZSBmaWxsPSIjYmViZWJlIiBjeD0iMzYiIGN5PSIxOCIgcj0iMSIvPjwvZz48L3N2Zz4=) 0 0/cover no-repeat;
                  bottom: -10px;
                  left: -10px;
                  position: absolute;
                  right: -10px;
                  top: -10px;
              }

              @-webkit-keyframes spinner {
                  0% {
                      -webkit-transform: rotate(0);
                      transform: rotate(0)
                  }
                  100% {
                      -webkit-transform: rotate(360deg);
                      transform: rotate(360deg)
                  }
              }

              @keyframes spinner {
                  0% {
                      -webkit-transform: rotate(0);
                      transform: rotate(0)
                  }
                  100% {
                      -webkit-transform: rotate(360deg);
                      transform: rotate(360deg)
                  }
              }
          </style>
          <div class="app-loader">
              <div class="loader-pusher">
                  <div class="spinner">
                      <div class="spinner-tail"></div>
                  </div>
              </div>
          </div>
      </app>
  </body>

</html>


================================================
FILE: apps/angular-i18next-demo/src/lib/validation/services/ValidationDirtyChecker.ts
================================================
import { Injectable } from '@angular/core';
import { FormArray, FormGroup } from '@angular/forms';

@Injectable()
export class ValidationDirtyChecker {
  markControlsDirty(group: FormGroup | FormArray) {
    const controls = group.controls;
    for (const ck in controls) {
      // eslint-disable-next-line no-prototype-builtins
      if (controls.hasOwnProperty(ck)) {
        const c = (<any>controls)[ck];
        c.markAsDirty({ onlySelf: true });
        if (c instanceof FormGroup || c instanceof FormArray)
          this.markControlsDirty(c);
      }
    }
  }
}


================================================
FILE: apps/angular-i18next-demo/src/lib/validation/validators/ArrayValidators.js
================================================
var ArrayValidators = (function () {
    function ArrayValidators() {
    }
    ArrayValidators.minLength = function (minLength, ignoreNullAndUndefined) {
        if (ignoreNullAndUndefined === void 0) { ignoreNullAndUndefined = false; }
        return function (control) {
            if (control) {
                var isArray = control.value instanceof Array;
                if (!isArray)
                    throw new Error('Control value must be array!');
                var val = control.value;
                var isValid = false;
                if (!ignoreNullAndUndefined)
                    isValid = val.length >= minLength;
                else
                    isValid = val.filter(function (v) { return v != null; }).length >= minLength;
                if (isValid)
                    return null;
                return { 'arrayMinLength': minLength };
            }
        };
    };
    ArrayValidators.maxLength = function (maxLength, ignoreNullAndUndefined) {
        if (ignoreNullAndUndefined === void 0) { ignoreNullAndUndefined = false; }
        return function (control) {
            if (control) {
                var isArray = control.value instanceof Array;
                if (!isArray)
                    throw new Error('Control value must be array!');
                var val = control.value;
                var isValid = false;
                if (!ignoreNullAndUndefined)
                    isValid = val.length <= maxLength;
                else
                    isValid = val.filter(function (v) { return v != null; }).length <= maxLength;
                if (isValid)
                    return null;
                return { 'arrayMaxLength': maxLength };
            }
        };
    };
    ArrayValidators.eqLength = function (length, ignoreNullAndUndefined) {
        if (ignoreNullAndUndefined === void 0) { ignoreNullAndUndefined = false; }
        return function (control) {
            if (control) {
                var isArray = control.value instanceof Array;
                if (!isArray)
                    throw new Error('Control value must be array!');
                var val = control.value;
                var isValid = false;
                if (!ignoreNullAndUndefined)
                    isValid = val.length === length;
                else
                    isValid = val.filter(function (v) { return v != null; }).length === length;
                if (isValid)
                    return null;
                return { 'arrayMaxLength': length };
            }
        };
    };
    return ArrayValidators;
}());
export { ArrayValidators };
//# sourceMappingURL=ArrayValidators.js.map

================================================
FILE: apps/angular-i18next-demo/src/lib/validation/validators/ArrayValidators.ts
================================================
import { OnChanges, SimpleChanges, Component, Directive, forwardRef } from '@angular/core';
import { Validator, AsyncValidatorFn, ValidatorFn, FormControl, FormGroup, FormArray, AbstractControl, NG_VALIDATORS } from '@angular/forms';

export class ArrayValidators {
  static minLength(minLength: Number, ignoreNullAndUndefined: Boolean = false): ValidatorFn {
    return (control: FormControl) => {
      if (control) {
        let isArray = control.value instanceof Array;
        if (!isArray)
          throw new Error('Control value must be array!');
        let val: Array<any> = control.value;
        let isValid: Boolean = false;
        if (!ignoreNullAndUndefined)
          isValid = val.length >= minLength;
        else
          isValid = val.filter(v => v != null).length >= minLength;
        if (isValid)
          return null;
        return { 'arrayMinLength': minLength };
      }
    };
  }
  static maxLength(maxLength: Number, ignoreNullAndUndefined: Boolean = false): ValidatorFn {
    return (control: FormControl) => {
      if (control) {
        let isArray = control.value instanceof Array;
        if (!isArray)
          throw new Error('Control value must be array!');
        let val: Array<any> = control.value;
        let isValid: Boolean = false;
        if (!ignoreNullAndUndefined)
          isValid = val.length <= maxLength;
        else
          isValid = val.filter(v => v != null).length <= maxLength;
        if (isValid)
          return null;
        return { 'arrayMaxLength': maxLength };
      }
    };
  }

  static eqLength(length: Number, ignoreNullAndUndefined: Boolean = false): ValidatorFn {
    return (control: FormControl) => {
      if (control) {
        let isArray = control.value instanceof Array;
        if (!isArray)
          throw new Error('Control value must be array!');
        let val: Array<any> = control.value;
        let isValid: Boolean = false;
        if (!ignoreNullAndUndefined)
          isValid = val.length === length;
        else
          isValid = val.filter(v => v != null).length === length;
        if (isValid)
          return null;
        return { 'arrayMaxLength': length };
      }
    };
  }
}


================================================
FILE: apps/angular-i18next-demo/src/lib/validation/validators/ConditionalValidator.js
================================================
import 'rxjs/add/operator/distinctUntilChanged'; // fn distinctUntilChanged
var ConditionalValidator = (function () {
    function ConditionalValidator() {
    }
    /**
    *  Валидатор, который применяет валидатор при некотором заданом условии.
    * @param {ConditionalFunc} conditional Условие для применения валидатора
    * @param {ValidatorFn} validator Валидатор, который будет применен
    * @param {Boolean} trackParentOnly Подписка только на изменение значения родителя (По-умолчанию подписка на root)
    */
    ConditionalValidator.set = function (conditional, validator, trackParentOnly) {
        if (trackParentOnly === void 0) { trackParentOnly = null; }
        var revalidateSub;
        return function (control) {
            if (control && control.parent) {
                if (!revalidateSub) {
                    revalidateOnChanges(control, trackParentOnly);
                    revalidateSub = true;
                }
                if (conditional(control.root)) {
                    return validator(control);
                }
            }
            return null;
        };
    };
    /* Не реализован */
    ConditionalValidator.setAsync = function (conditional, validator) {
        throw new Error('Not implemented'); // todo: implement
    };
    ConditionalValidator.equivalent = function (controlKey, expectedValue) {
        return function (rootGroup) {
            var control = rootGroup.get(controlKey);
            if (!control)
                return expectedValue === undefined;
            return expectedValue === control.value;
        };
    };
    return ConditionalValidator;
}());
export { ConditionalValidator };
function revalidateOnChanges(control, trackParentOnly) {
    if (trackParentOnly === void 0) { trackParentOnly = null; }
    var parentControl = trackParentOnly ? control.parent : control.root;
    parentControl.valueChanges
        .distinctUntilChanged(function (a, b) {
        // These will always be plain objects coming from the form, do a simple comparison
        if (a && !b || !a && b) {
            return false;
        }
        else if (a && b && Object.keys(a).length !== Object.keys(b).length) {
            return false;
        }
        else if (a && b) {
            for (var i in a) {
                if (a[i] !== b[i]) {
                    return false;
                }
            }
        }
        return true;
    })
        .subscribe(function () {
        control.updateValueAndValidity({ onlySelf: true, emitEvent: false });
    });
}
//# sourceMappingURL=ConditionalValidator.js.map

================================================
FILE: apps/angular-i18next-demo/src/lib/validation/validators/ConditionalValidator.ts
================================================
import { Validator, AsyncValidatorFn, ValidatorFn, FormControl, FormGroup, FormArray, AbstractControl } from '@angular/forms';
import { distinctUntilChanged } from 'rxjs/operators';

// todo: доработать ConditionalValidator, чтобы он работал в связке с асинхронным валидатором (сейчас валится)

/* usage

this.formBuilder.group({
    vehicleType: ['', Validators.required],
    licencePlate: [
        '',
        ConditionalValidator.apply(
            group => group.controls.vehicleType.value === 'car',
            Validators.compose([
                Validators.required,
                Validators.minLength(6)
            ])
        ),
    ]
});


this.formBuilder.group({
    country: ['', Validators.required],
    vehicleType: ['', Validators.required],
    licencePlate: [
        '',
        Validators.compose([
            ConditionalValidator.apply(
                group => group.controls.vehicleType.value === 'car',
                Validators.required
            ),
            ConditionalValidator.apply(
                group => group.controls.country.value === 'sweden',
                Validators.minLength(6)
            ),
        ])
    ]
});

*/


interface ConditionalFunc {
  (rootGroup: FormGroup | FormArray): Boolean;
}


export class ConditionalValidator {
  /**
  *  Валидатор, который применяет валидатор при некотором заданом условии.
  * @param {ConditionalFunc} conditional Условие для применения валидатора
  * @param {ValidatorFn} validator Валидатор, который будет применен
  * @param {Boolean} trackParentOnly Подписка только на изменение значения родителя (По-умолчанию подписка на root)
  */
  static set(conditional: ConditionalFunc, validator: ValidatorFn, trackParentOnly: Boolean = null): ValidatorFn {
    let revalidateSub: Boolean;
    return (control: FormControl) => {
      if (control && control.parent) {
        if (!revalidateSub) {
          revalidateOnChanges(control, trackParentOnly);
          revalidateSub = true;
        }
        if (conditional(<FormGroup|FormArray>control.root)) {
          return validator(control);
        }
      }
      return null;
    };
  }

  /* Не реализован */
  static setAsync(conditional: Function, validator: AsyncValidatorFn): AsyncValidatorFn {
    throw new Error('Not implemented'); // todo: implement
  }

  static equivalent(controlKey: string, expectedValue: any): ConditionalFunc {
    return (rootGroup: FormGroup|FormArray) => {
      let control = rootGroup.get(controlKey);
      if (!control)
        return expectedValue === undefined;
      return expectedValue === control.value;
    }
  }
}

function revalidateOnChanges(control: AbstractControl, trackParentOnly: Boolean = null): void {
  let parentControl = trackParentOnly ? control.parent : control.root;
  parentControl.valueChanges
    .pipe(
        distinctUntilChanged((a, b) => {
          // These will always be plain objects coming from the form, do a simple comparison
          if (a && !b || !a && b) {
            return false;
          } else if (a && b && Object.keys(a).length !== Object.keys(b).length) {
            return false;
          } else if (a && b) {
            for (let i in a) {
              if (a[i] !== b[i]) {
                return false;
              }
            }
          }
          return true;
        })
    )
    .subscribe(() => {
      control.updateValueAndValidity({ onlySelf: true, emitEvent: false });
    });
}



================================================
FILE: apps/angular-i18next-demo/src/locales/en.error.json
================================================
{
  "oops": "Oops!",
  "error_occured": "Error has occured",
  "error_occured_onload": "$t(error:error_occured)",
  "access_denied": "Access is denied",
  "reload": "Reload",
  "restart": "Restart",
  "contact_administrator_or_try_to_clear_browser_chache_and_restart_application": "Contact your administrator or clear browser cache and restart page.",
  "need_help_write_to_us": "Need help? Contact us.",
  "write": "Contact",
  "cookies": {
    "how_to": "How to clear browser cache and cookies",
    "chrome_clear": "Если у вас Google Chrome, то:<ol><li>Запустите Chrome.</li><li>Нажмите на значок&nbsp;<img src=\"//storage.googleapis.com/support-kms-prod/5C6FB52C8BBB2C12DC89B5F42F16B9B5E9CF\" width=\"18\" height=\"18\" alt=\"Настройка и управление Google Chrome\" title=\"Настройка и управление Google Chrome\"> на панели инструментов.</li><li>В меню <strong>Дополнительные инструменты</strong> нажмите <strong>Удаление данных о просмотренных страницах</strong>.</li><li>В окне \"Очистить историю\" выберите пункты <strong>Файлы cookie, а также другие данные сайтов и плагинов</strong> и <strong>Изображения и другие файлы, сохраненные в кеше</strong>.</li><li>В раскрывающемся меню в верхней части страницы выберите период, данные за который нужно удалить. Выберите вариант <strong>за все время</strong>, если вы хотите удалить все сведения.</li><li>Нажмите кнопку <strong>Очистить историю</strong>.</li></ol>"
  }
}


================================================
FILE: apps/angular-i18next-demo/src/locales/en.feature.rich_form.json
================================================
{
    "title": "Rich form with validation",
    "technical_contact": "technical contact",
    "count": "count",
    "person": {
        "first_name": "first name",
        "last_name": "last name",
        "middle_name": "middle name"
    }
}


================================================
FILE: apps/angular-i18next-demo/src/locales/en.translation.json
================================================
{
    "application_title": "Demo: angular-i18next",
    "intro": "This application is demonstrating itegration of i18next library with angular. You can switch language in the navbar.",
    "simple_demo": "Simple demo",
    "rich_form_title": "Rich form with validation",
    "parametrized_string_title": "parametrized string demo",
    "case_demo_title": "i18next pipe 'format' option demo",
    "case_demo": "rise and shine, Mr.Freeman",
    "parametrized_string": "I am parametrized sting with a value: {{value}} and a string: '{{str}}'",
    "email": "email",
    "_languages": {
        "ru": "Русский",
        "en": "English"
    },
    "buttons": {
        "send": "send"
    }
}


================================================
FILE: apps/angular-i18next-demo/src/locales/en.validation.json
================================================
{
    "required": "Field is required.",
    "error": "Error occured.",
    "min": "Minimum value is {{min}}. Was {{actual}}.",
    "max": "Maximum value is {{max}}. Was {{actual}}.",
    "email": "$t(validation:_fill) valid e-mail.",
    "pattern": "$t(validation:_fill) valid value.",
    "maxlength": "Maximum length {{requiredLength}}.",

    "_fill": "Please fill in",
    "control_specific": {
        "technicalContact": {
            "firstName": {
                "required": "$t(validation:_fill) technical specialist's first name."
            },
            "lastName": {
                "required": "$t(validation:_fill) technical specialist's last name."
            },
            "middleName": {
                "required": "$t(validation:_fill) technical specialist's patronymic."
            }
        }
    }
}

================================================
FILE: apps/angular-i18next-demo/src/locales/ru.error.json
================================================
{
  "oops": "Упс!",
  "error_occured": "произошла ошибка",
  "error_occured_onload": "При загрузке приложения $t(error:error_occured)",
  "access_denied": "Недостаточно прав",
  "reload": "Перезагрузить",
  "restart": "Перезапустить",
  "contact_administrator_or_try_to_clear_browser_chache_and_restart_application": "Обратитесь к администратору либо попробуйте очистить кэш и перезапустить приложение",
  "need_help_write_to_us": "Нужна помощь? Пишите нам.",
  "write": "Написать",
  "cookies": {
    "how_to": "Как очистить кэш и удалить файлы cookie",
    "chrome_clear": "Если у вас Google Chrome, то:<ol><li>Запустите Chrome.</li><li>Нажмите на значок&nbsp;<img src=\"//storage.googleapis.com/support-kms-prod/5C6FB52C8BBB2C12DC89B5F42F16B9B5E9CF\" width=\"18\" height=\"18\" alt=\"Настройка и управление Google Chrome\" title=\"Настройка и управление Google Chrome\"> на панели инструментов.</li><li>В меню <strong>Дополнительные инструменты</strong> нажмите <strong>Удаление данных о просмотренных страницах</strong>.</li><li>В окне \"Очистить историю\" выберите пункты <strong>Файлы cookie, а также другие данные сайтов и плагинов</strong> и <strong>Изображения и другие файлы, сохраненные в кеше</strong>.</li><li>В раскрывающемся меню в верхней части страницы выберите период, данные за который нужно удалить. Выберите вариант <strong>за все время</strong>, если вы хотите удалить все сведения.</li><li>Нажмите кнопку <strong>Очистить историю</strong>.</li></ol>"
  }
}


================================================
FILE: apps/angular-i18next-demo/src/locales/ru.feature.rich_form.json
================================================
{
    "title": "Форма с валидацией",
    "technical_contact": "технический специалист",
    "count": "кол-во",
    "person": {
        "first_name": "имя",
        "last_name": "фамилия",
        "middle_name": "отчество"
    }
}


================================================
FILE: apps/angular-i18next-demo/src/locales/ru.translation.json
================================================
{
    "application_title": "Демо: angular-i18next",
    "intro": "Данное приложение демонстрирует интеграцию библиотеки i18next с angular. Вы можете сменить язык в шапке.",
    "simple_demo": "простое демо",
    "rich_form_title": "Форма с валидацией",
    "parametrized_string_title": "демонстрация параметризованной строки",
    "case_demo_title": "демонстрация опции 'format'",
    "case_demo": "проснись и пой, мистер Фримэн",
    "parametrized_string": "Я параметризованная строка значением: {{value}} и строкой: '{{str}}'",
    "email": "email адрес",
    "_languages": {
        "ru": "Русский",
        "en": "English"
    },
    "buttons": {
        "send": "отправить"
    }
}


================================================
FILE: apps/angular-i18next-demo/src/locales/ru.validation.json
================================================
{
    "required": "Заполните это поле.",
    "error": "Возникла ошибка.",
    "minValue": "Минимальное значение.",
    "maxValue": "Максимальное значение.",
    "min": "Минимальное значение: {{min}}. Текущее: {{actual}}.",
    "max": "Максимальное значение: {{max}}. Текущее: {{actual}}.",
    "email": "Введите валидный email.",
    "pattern": "Введите валидное значение.",
    "maxlength": "Максимальная длина {{requiredLength}}.",

    "control_specific": {
        "technicalContact": {
            "firstName": {
                "required": "Заполните имя технического специалиста.",
                "pattern": "Имя технического специалиста содержит недопустимые символы."
            },
            "lastName": {
                "required": "Заполните фамилию технического специалиста.",
                "pattern": "Фамилия технического специалиста содержит недопустимые символы."
            },
            "middleName": {
                "required": "Заполните отчество технического специалиста.",
                "pattern": "Отчество технического специалиста содержит недопустимые символы."
            }
        }
    }
}

================================================
FILE: apps/angular-i18next-demo/src/main.server.ts
================================================
import { bootstrapApplication, BootstrapContext } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { config } from './app/app.config.server';


const bootstrap = (context: BootstrapContext) => bootstrapApplication(AppComponent, config, context);
export default bootstrap;


================================================
FILE: apps/angular-i18next-demo/src/main.ts
================================================
import { enableProdMode } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

function bootstrap() {
  bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));
}

if (document.readyState === 'complete') {
  bootstrap();
} else {
  document.addEventListener('DOMContentLoaded', bootstrap);
}


================================================
FILE: apps/angular-i18next-demo/src/server.ts
================================================
import {
  AngularNodeAppEngine,
  createNodeRequestHandler,
  isMainModule,
  writeResponseToNodeResponse,
} from '@angular/ssr/node';
import type { NextFunction, Request, Response } from 'express';
import express from 'express';
import i18next from 'i18next';
import ChainedBackend, { ChainedBackendOptions } from 'i18next-chained-backend';
import * as i18nextHttpMiddleware from 'i18next-http-middleware';
import resourcesToBackend from "i18next-resources-to-backend";
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import i18nextOptions from './app/i18next.options';


const serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, '../browser');

const app = express();
const angularApp = new AngularNodeAppEngine();

await i18next
    .use(ChainedBackend)
    .use(i18nextHttpMiddleware.LanguageDetector)
    .init<ChainedBackendOptions>({
      ...i18nextOptions,
      backend: {
        backends: [
          resourcesToBackend((lng, ns, clb) => {
            import(`./locales/${lng}.${ns}.json`)
                  .then((resources) => clb(null, resources))
                  .catch((r)=> clb(r,null))
          })
        ],
        backendOptions: [{
          loadPath: '/locales/{{lng}}.{{ns}}.json'
        }]
      }
  });

const i18nextHandler = i18nextHttpMiddleware.handle(i18next) as any;
app.use(i18nextHandler);
/**
 * Example Express Rest API endpoints can be defined here.
 * Uncomment and define endpoints as necessary.
 *
 * Example:
 * ```ts
 * app.get('/api/**', (req, res) => {
 *   // Handle API request
 * });
 * ```
 */

/**
 * Serve static files from /browser
 */
app.use(
  express.static(browserDistFolder, {
    maxAge: '1y',
    index: false,
    redirect: false,
  }),
);

/**
 * Handle all other requests by rendering the Angular application.
 */
app.use('/**', (req: Request & i18nextHttpMiddleware.I18NextRequest, res: Response, next: NextFunction) => {
  angularApp
    .handle(req, {
      i18n: req.i18n,
    })
    .then((response) =>
      response ? writeResponseToNodeResponse(response, res) : next(),
    )
    .catch(next);
});

/**
 * Start the server if this module is the main entry point.
 * The server listens on the port defined by the `PORT` environment variable, or defaults to 4000.
 */
if (isMainModule(import.meta.url) || process.env['PM2'] === 'true') {
  const port = process.env['PORT'] || 4000;
  const server = app.listen(port, () => {
    process.send?.('ready');
    console.log(`Node Express server listening on http://localhost:${port}`);
  });

  // Graceful shutdown
  process.on('SIGINT', () => {
      const cleanUp = () => {
        // Clean up other resources like DB connections
      }

      console.log('Closing server...')

      server.close(() => {
        console.log('Server closed !!! ')

        cleanUp()
        process.exit()
      })

      // Force close server after 5secs
      setTimeout((e: any) => {
        console.log('Forcing server close !!!', e)

        cleanUp()
        process.exit(1)
      }, 5000)
    })
  }

/**
 * The request handler used by the Angular CLI (dev-server and during build).
 */
export const reqHandler = createNodeRequestHandler(app);


================================================
FILE: apps/angular-i18next-demo/src/styles.css
================================================
/* You can add global styles to this file, and also import other style files */

input.ng-dirty.ng-invalid,
textarea.ng-dirty.ng-invalid,
.form-control.ng-dirty.ng-invalid,
.ng-dirty.ng-invalid:focus
{
    background-color: #FDEDED;
    border-color: #D22630;
    -webkit-box-shadow: none;
    box-shadow: none;
}

/* s7 ui kit fix */
.form-group .error-container {
  display: block;
}

.error-container {
	color: #D22630;
	padding-top: 2px;
}


/* end fix */

checkbox.ng-invalid .custom-control-indicator,
multiplecheckbox.ng-invalid.ng-dirty .custom-control-indicator,
flatpickr.ng-invalid.ng-dirty .form-control,
datepicker.ng-invalid.ng-dirty .select2-container .select2-selection,
radio-button.ng-dirty.ng-invalid .custom-control-indicator,
div.ng-invalid.ng-dirty.form-group-valid .custom-control-indicator,
div.ng-invalid.ng-dirty.form-group-valid .select2-container .select2-selection {
    border-color: #D22630;
    background-color: #FDEDED;
}

radio-button + radio-button,
checkbox + checkbox {
  margin-left: 15px;
}




================================================
FILE: apps/angular-i18next-demo/src/test-setup.ts
================================================
import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless';

setupZonelessTestEnv();


================================================
FILE: apps/angular-i18next-demo/tsconfig.app.json
================================================
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "../../dist/out-tsc",
    "types": ["node"]
  },
  "files": [
    "src/main.ts",
    "src/main.server.ts",
    "src/server.ts"
  ],
  "include": ["src/**/*.d.ts"],
  "exclude": ["**/*.test.ts", "**/*.spec.ts"]
}


================================================
FILE: apps/angular-i18next-demo/tsconfig.editor.json
================================================
{
  "extends": "./tsconfig.json",
  "include": ["**/*.ts"],
  "compilerOptions": {
    "types": ["jest", "node"]
  }
}


================================================
FILE: apps/angular-i18next-demo/tsconfig.json
================================================
{
  "extends": "../../tsconfig.base.json",
  "files": [],
  "include": [],
  "references": [
    {
      "path": "./tsconfig.app.json"
    },
    {
      "path": "./tsconfig.spec.json"
    },
    {
      "path": "./tsconfig.editor.json"
    }
  ],
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "noImplicitOverride": true,
    "noPropertyAccessFromIndexSignature": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true
  },
  "angularCompilerOptions": {
    "enableI18nLegacyMessageIdFormat": false,
    "strictInjectionParameters": true,
    "strictInputAccessModifiers": true,
    "strictTemplates": true
  }
}


================================================
FILE: apps/angular-i18next-demo/tsconfig.server.json
================================================
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
  "extends": "./tsconfig.app.json",
  "compilerOptions": {
    "outDir": "../../out-tsc/server",
    "target": "es2019",
    "types": [
      "node"
    ],
    "allowSyntheticDefaultImports": true,
  },
  "files": [
    "src/main.server.ts",
    "server.ts"
  ],
  "angularCompilerOptions": {
    "entryModule": "./src/app/app.server.module#AppServerModule"
  }
}


================================================
FILE: apps/angular-i18next-demo/tsconfig.spec.json
================================================
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "../../dist/out-tsc",
    "module": "commonjs",
    "types": ["jest", "node"],
    "allowSyntheticDefaultImports": true, // Typing support for this case
    "esModuleInterop": true,
  },
  "files": ["src/test-setup.ts"],
  "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"]
}


================================================
FILE: decorate-angular-cli.js
================================================
/**
 * This file decorates the Angular CLI with the Nx CLI to enable features such as computation caching
 * and faster execution of tasks.
 *
 * It does this by:
 *
 * - Patching the Angular CLI to warn you in case you accidentally use the undecorated ng command.
 * - Symlinking the ng to nx command, so all commands run through the Nx CLI
 * - Updating the package.json postinstall script to give you control over this script
 *
 * The Nx CLI decorates the Angular CLI, so the Nx CLI is fully compatible with it.
 * Every command you run should work the same when using the Nx CLI, except faster.
 *
 * Because of symlinking you can still type `ng build/test/lint` in the terminal. The ng command, in this case,
 * will point to nx, which will perform optimizations before invoking ng. So the Angular CLI is always invoked.
 * The Nx CLI simply does some optimizations before invoking the Angular CLI.
 *
 * To opt out of this patch:
 * - Replace occurrences of nx with ng in your package.json
 * - Remove the script from your postinstall script in your package.json
 * - Delete and reinstall your node_modules
 */

const fs = require('fs');
const os = require('os');
const cp = require('child_process');
const isWindows = os.platform() === 'win32';
let output;
try {
  output = require('@nx/workspace').output;
} catch (e) {
  console.warn(
    'Angular CLI could not be decorated to enable computation caching. Please ensure @nx/workspace is installed.'
  );
  process.exit(0);
}

/**
 * Symlink of ng to nx, so you can keep using `ng build/test/lint` and still
 * invoke the Nx CLI and get the benefits of computation caching.
 */
function symlinkNgCLItoNxCLI() {
  try {
    const ngPath = './node_modules/.bin/ng';
    const nxPath = './node_modules/.bin/nx';
    if (isWindows) {
      /**
       * This is the most reliable way to create symlink-like behavior on Windows.
       * Such that it works in all shells and works with npx.
       */
      ['', '.cmd', '.ps1'].forEach((ext) => {
        if (fs.existsSync(nxPath + ext))
          fs.writeFileSync(ngPath + ext, fs.readFileSync(nxPath + ext));
      });
    } else {
      // If unix-based, symlink
      cp.execSync(`ln -sf ./nx ${ngPath}`);
    }
  } catch (e) {
    output.error({
      title:
        'Unable to create a symlink from the Angular CLI to the Nx CLI:' +
        e.message,
    });
    throw e;
  }
}

try {
  symlinkNgCLItoNxCLI();
  require('@nrwl/cli/lib/decorate-cli').decorateCli();
  output.log({
    title: 'Angular CLI has been decorated to enable computation caching.',
  });
} catch (e) {
  output.error({
    title: 'Decoration of the Angular CLI did not complete successfully',
  });
}


================================================
FILE: ecosystem.config.js
================================================
const { cwd } = require("process");

module.exports = {
  apps: [
    {
      name: 'angular-i18next-demo',
      script: 'server.mjs',
      cwd: './dist/angular-i18next-demo/server',
      max_memory_restart: '100M',
      env: {
        PM2: true,
        NODE_ENV: "development"
      },
      env_production: {
        PM2: true,
        NODE_ENV: "production",
      }
    },
  ],
};


================================================
FILE: jest.config.ts
================================================

export default {
  projects: ['./libs/**/jest.config.ts'],
};


================================================
FILE: jest.preset.js
================================================
const nxPreset = require('@nx/jest/preset').default;

module.exports = {
  ...nxPreset,
  /* TODO: Update to latest Jest snapshotFormat
   * By default Nx has kept the older style of Jest Snapshot formats
   * to prevent breaking of any existing tests with snapshots.
   * It's recommend you update to the latest format.
   * You can do this by removing snapshotFormat property
   * and running tests with --update-snapshot flag.
   * Example: "nx affected --targets=test --update-snapshot"
   * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
   */
  snapshotFormat: { escapeString: true, printBasicPrototype: true },
};


================================================
FILE: libs/angular-i18next/.eslintrc.json
================================================
{
  "extends": ["../../.eslintrc.json"],
  "ignorePatterns": ["!**/*"]
}


================================================
FILE: libs/angular-i18next/CHANGELOG.md
================================================
# [21.0.0-3](https://github.com/Romanchuk/angular-i18next/compare/v20.0.1...v21.0.0-3) (2026-01-23)


### Bug Fixes

* build ([4f8d746](https://github.com/Romanchuk/angular-i18next/commit/4f8d746e88401bc339250af3383097f6837823cb))
* jest test setup ([253a5eb](https://github.com/Romanchuk/angular-i18next/commit/253a5eb47f55428e81ec43c0c48fff2de3036205))
* units ([2a46eda](https://github.com/Romanchuk/angular-i18next/commit/2a46eda0df9e117357965e7499573a88992fef5b))



# [21.0.0-2](https://github.com/Romanchuk/angular-i18next/compare/v20.0.1...v21.0.0-2) (2026-01-23)


### Bug Fixes

* build ([4f8d746](https://github.com/Romanchuk/angular-i18next/commit/4f8d746e88401bc339250af3383097f6837823cb))
* jest test setup ([253a5eb](https://github.com/Romanchuk/angular-i18next/commit/253a5eb47f55428e81ec43c0c48fff2de3036205))
* units ([2a46eda](https://github.com/Romanchuk/angular-i18next/commit/2a46eda0df9e117357965e7499573a88992fef5b))



# [21.0.0-1](https://github.com/Romanchuk/angular-i18next/compare/v20.0.1...v21.0.0-1) (2026-01-23)


### Bug Fixes

* build ([4f8d746](https://github.com/Romanchuk/angular-i18next/commit/4f8d746e88401bc339250af3383097f6837823cb))
* jest test setup ([253a5eb](https://github.com/Romanchuk/angular-i18next/commit/253a5eb47f55428e81ec43c0c48fff2de3036205))
* units ([2a46eda](https://github.com/Romanchuk/angular-i18next/commit/2a46eda0df9e117357965e7499573a88992fef5b))



## [20.0.1](https://github.com/Romanchuk/angular-i18next/compare/v20.0.1-0...v20.0.1) (2025-11-14)



## [20.0.1-0](https://github.com/Romanchuk/angular-i18next/compare/v20.0.0...v20.0.1-0) (2025-11-14)


### Bug Fixes

* i18next typings ([9dabeb9](https://github.com/Romanchuk/angular-i18next/commit/9dabeb9ab832413bb488151eb6f74bd65c9a48e8))



# [20.0.0](https://github.com/Romanchuk/angular-i18next/compare/v19.1.1...v20.0.0) (2025-08-22)


### Features

* **up version i18next 25:** up version i18next 25 ([4a8d23a](https://github.com/Romanchuk/angular-i18next/commit/4a8d23a016ca4daa5bf06255b24ec10faa004948))


### BREAKING CHANGES

* **up version i18next 25:** up version i18next 25



## [19.1.1](https://github.com/Romanchuk/angular-i18next/compare/v19.1.1-0...v19.1.1) (2025-08-22)



## [19.1.1-0](https://github.com/Romanchuk/angular-i18next/compare/v19.1.0...v19.1.1-0) (2025-06-22)


### Bug Fixes

* ci ([f5c5339](https://github.com/Romanchuk/angular-i18next/commit/f5c5339598c340c2e7f973f90f06dd73c85c621b))
* ci ([563993b](https://github.com/Romanchuk/angular-i18next/commit/563993b507873903a6caf4374508686d8b1b5fd0))
* ci ([443db4e](https://github.com/Romanchuk/angular-i18next/commit/443db4e748522519bd1057947651e724187b1392))
* prepare demo ([a80d2bc](https://github.com/Romanchuk/angular-i18next/commit/a80d2bc19cfed95c5c5a7c4cf40a560f720353af))
* tsconfig.spec ([cea853b](https://github.com/Romanchuk/angular-i18next/commit/cea853bdab70b7199f05896a43deed9c26e26088))



# [19.1.0](https://github.com/Romanchuk/angular-i18next/compare/v19.0.1...v19.1.0) (2025-03-03)


### Bug Fixes

* browser build and serve ([d9b73f2](https://github.com/Romanchuk/angular-i18next/commit/d9b73f20b82f5ba1d0692c40787e47032f30367e))
* build ([300e975](https://github.com/Romanchuk/angular-i18next/commit/300e975a1c6072e0564c5fff50e4bf1ddb7f4751))
* build ([ed46b54](https://github.com/Romanchuk/angular-i18next/commit/ed46b542564070d462b8d5b3c0b92b1e4a04ac55))
* forms ([3781a6b](https://github.com/Romanchuk/angular-i18next/commit/3781a6b7786b4666a77402cefec13860dacc4b05))
* new provide ([f17a4f8](https://github.com/Romanchuk/angular-i18next/commit/f17a4f863b422c667d76457bc4c996760a3c1ca0))
* specs ([5e12691](https://github.com/Romanchuk/angular-i18next/commit/5e12691a4dc091f251d0e254f36d8fa568a22158))
* ssr ([822b4d1](https://github.com/Romanchuk/angular-i18next/commit/822b4d1950acdadc2a5dabcbdd3a9983d9b15acd))
* tests ([7bba2bc](https://github.com/Romanchuk/angular-i18next/commit/7bba2bc353eab68a867c8bef62e7da28b9557f58))



# [19.1.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v19.0.1...v19.1.0-beta) (2025-03-01)


### Bug Fixes

* browser build and serve ([d9b73f2](https://github.com/Romanchuk/angular-i18next/commit/d9b73f20b82f5ba1d0692c40787e47032f30367e))
* build ([300e975](https://github.com/Romanchuk/angular-i18next/commit/300e975a1c6072e0564c5fff50e4bf1ddb7f4751))
* build ([ed46b54](https://github.com/Romanchuk/angular-i18next/commit/ed46b542564070d462b8d5b3c0b92b1e4a04ac55))
* forms ([3781a6b](https://github.com/Romanchuk/angular-i18next/commit/3781a6b7786b4666a77402cefec13860dacc4b05))
* new provide ([f17a4f8](https://github.com/Romanchuk/angular-i18next/commit/f17a4f863b422c667d76457bc4c996760a3c1ca0))
* specs ([5e12691](https://github.com/Romanchuk/angular-i18next/commit/5e12691a4dc091f251d0e254f36d8fa568a22158))
* ssr ([822b4d1](https://github.com/Romanchuk/angular-i18next/commit/822b4d1950acdadc2a5dabcbdd3a9983d9b15acd))
* tests ([7bba2bc](https://github.com/Romanchuk/angular-i18next/commit/7bba2bc353eab68a867c8bef62e7da28b9557f58))



## [19.0.1](https://github.com/Romanchuk/angular-i18next/compare/v19.0.0...v19.0.1) (2025-01-11)



# [19.0.0](https://github.com/Romanchuk/angular-i18next/compare/v19.0.0-0...v19.0.0) (2025-01-11)


### Bug Fixes

* app ([b634439](https://github.com/Romanchuk/angular-i18next/commit/b63443967f5da8bb470c09871562d431a2af2cf3))



# [19.0.0-0](https://github.com/Romanchuk/angular-i18next/compare/v18.0.0...v19.0.0-0) (2025-01-11)


### Bug Fixes

* pages ([27d6e56](https://github.com/Romanchuk/angular-i18next/commit/27d6e5644d9c9d50d88fc7ab7bdc3d23f94285ef))
* tests ([29abe20](https://github.com/Romanchuk/angular-i18next/commit/29abe20a002ec9912af0388abf4a3f5eb0a97d90))



# [18.0.0](https://github.com/Romanchuk/angular-i18next/compare/v18.0.0-0...v18.0.0) (2024-06-03)



# [18.0.0-0](https://github.com/Romanchuk/angular-i18next/compare/v17.0.2...v18.0.0-0) (2024-06-03)



## [17.0.2](https://github.com/Romanchuk/angular-i18next/compare/v17.0.1...v17.0.2) (2024-06-03)


### Bug Fixes

* tests ([50c5f38](https://github.com/Romanchuk/angular-i18next/commit/50c5f38b122755d1a33fa0db0a59f7be201f26d3))



## [17.0.1](https://github.com/Romanchuk/angular-i18next/compare/v17.0.0...v17.0.1) (2023-12-07)


### Bug Fixes

* nx and jest setup ([0e6a61d](https://github.com/Romanchuk/angular-i18next/commit/0e6a61dd882f103b40bce38577fd7e7bcf44309a))



# [17.0.0](https://github.com/Romanchuk/angular-i18next/compare/v17.0.0-1...v17.0.0) (2023-12-01)



# [17.0.0-1](https://github.com/Romanchuk/angular-i18next/compare/v17.0.0-0...v17.0.0-1) (2023-11-28)



# [17.0.0-0](https://github.com/Romanchuk/angular-i18next/compare/v16.0.0...v17.0.0-0) (2023-11-27)



# [16.0.0](https://github.com/Romanchuk/angular-i18next/compare/v16.0.0-0...v16.0.0) (2023-06-09)



# [16.0.0-0](https://github.com/Romanchuk/angular-i18next/compare/v15.0.5...v16.0.0-0) (2023-06-09)


### Bug Fixes

* package.json ([4f3d909](https://github.com/Romanchuk/angular-i18next/commit/4f3d909321651faa4f6406cf300582ce8a8001ce))
* test ([991dbca](https://github.com/Romanchuk/angular-i18next/commit/991dbca3c5ad9807b4884727065effe456f56b61))
* test script ([b075485](https://github.com/Romanchuk/angular-i18next/commit/b0754858d865a5e4aaa80ac18fb264d276f26787))



## [15.0.5](https://github.com/Romanchuk/angular-i18next/compare/v15.0.4...v15.0.5) (2023-01-24)


### Bug Fixes

* [#101](https://github.com/Romanchuk/angular-i18next/issues/101) ([e7d095a](https://github.com/Romanchuk/angular-i18next/commit/e7d095a2336b663f95e08ddadbe65de6cf8b191c))



## [15.0.4](https://github.com/Romanchuk/angular-i18next/compare/v15.0.3...v15.0.4) (2023-01-16)



## [15.0.3](https://github.com/Romanchuk/angular-i18next/compare/v15.0.1...v15.0.3) (2023-01-16)



## [15.0.2](https://github.com/Romanchuk/angular-i18next/compare/v15.0.1...v15.0.2) (2023-01-16)



## [15.0.1](https://github.com/Romanchuk/angular-i18next/compare/v15.0.0...v15.0.1) (2023-01-16)


### Bug Fixes

* [#97](https://github.com/Romanchuk/angular-i18next/issues/97) "strictNullChecks": true ([92d8205](https://github.com/Romanchuk/angular-i18next/commit/92d8205003908cb89587a99268983184fa4d6316))
* t signature ([029478a](https://github.com/Romanchuk/angular-i18next/commit/029478a5582afe626d892b8e1ee59c7d08e544f5))



# [15.0.0](https://github.com/Romanchuk/angular-i18next/compare/v15.0.0-1...v15.0.0) (2023-01-16)



# [15.0.0-1](https://github.com/Romanchuk/angular-i18next/compare/v15.0.0-0...v15.0.0-1) (2023-01-16)



# [15.0.0-0](https://github.com/Romanchuk/angular-i18next/compare/v14.2.0...v15.0.0-0) (2022-12-22)


### Bug Fixes

* pages deploy ([df17d58](https://github.com/Romanchuk/angular-i18next/commit/df17d58fcfd3a18c9862064d43e33e799effb84e))



# [14.2.0](https://github.com/Romanchuk/angular-i18next/compare/v14.2.0-1...v14.2.0) (2022-11-22)



# [14.2.0-1](https://github.com/Romanchuk/angular-i18next/compare/v14.2.0-0...v14.2.0-1) (2022-11-21)


### Bug Fixes

* factory type ([7a0c62c](https://github.com/Romanchuk/angular-i18next/commit/7a0c62cac779149c9b26b8cbd502538457d7159e))
* missed import ([e793bc5](https://github.com/Romanchuk/angular-i18next/commit/e793bc511227f887d677bd2f45f3b2b173977a8e))
* specs default import ([532dd94](https://github.com/Romanchuk/angular-i18next/commit/532dd94bc23f57f0e3e0256a3534f0c48381f12f))



# [14.2.0-0](https://github.com/Romanchuk/angular-i18next/compare/v14.1.0...v14.2.0-0) (2022-11-17)


### Bug Fixes

* i18next instance ([93e48b6](https://github.com/Romanchuk/angular-i18next/commit/93e48b646c486d9e157447a704d88925e05957c6))
* jest default import ([cea6677](https://github.com/Romanchuk/angular-i18next/commit/cea66776dd6af912dfdfff500f14bbe8e17c81e7))
* link to global i18next ([4439d3a](https://github.com/Romanchuk/angular-i18next/commit/4439d3a19ef3d2bec7fcb296b8f3eaebbd1af6a8))
* tests ([78bc41e](https://github.com/Romanchuk/angular-i18next/commit/78bc41e3a4eca2b63f28513120f0ef9863d2a21a))



# [14.1.0](https://github.com/Romanchuk/angular-i18next/compare/v14.0.5-6...v14.1.0) (2022-11-09)


### Bug Fixes

* messages ([1cbebea](https://github.com/Romanchuk/angular-i18next/commit/1cbebea76d188057070442d6ffbd62bbf44ccf09))
* setup ([86de471](https://github.com/Romanchuk/angular-i18next/commit/86de471f898068274355d87f1c084ce76bb72b91))



## [14.0.5-6](https://github.com/Romanchuk/angular-i18next/compare/v14.0.0...v14.0.5-6) (2022-11-07)


### Bug Fixes

* async funcs ([25e2c24](https://github.com/Romanchuk/angular-i18next/commit/25e2c24ba8c0cce1d4b235d1449fb554937b6fe4))
* build ([aafe356](https://github.com/Romanchuk/angular-i18next/commit/aafe356437287a4a2e30668a726c18e5a118cc04))
* cpy ([2a2460e](https://github.com/Romanchuk/angular-i18next/commit/2a2460e8e53f5a18185bf979318e0be77019f82e))
* Fixes i18next format call with options undefined ([ccfc6e1](https://github.com/Romanchuk/angular-i18next/commit/ccfc6e1583fcb4a5a8b591ac0e3e8cf95ce675eb))
* package ([9b03de0](https://github.com/Romanchuk/angular-i18next/commit/9b03de01029432a679b84f0b535c8c0986a2ce49))
* test run ([cc2ca1c](https://github.com/Romanchuk/angular-i18next/commit/cc2ca1c6dbef4fe1126de9ffb7c73f0dc2be9062))
* tests ([0b868fc](https://github.com/Romanchuk/angular-i18next/commit/0b868fc25b9c7ff4902308425e066835d117596d))



## [14.0.5-3](https://github.com/Romanchuk/angular-i18next/compare/v14.0.0...v14.0.5-3) (2022-07-05)


### Bug Fixes

* package ([9b03de0](https://github.com/Romanchuk/angular-i18next/commit/9b03de01029432a679b84f0b535c8c0986a2ce49))



## [14.0.5-2](https://github.com/Romanchuk/angular-i18next/compare/v14.0.0...v14.0.5-2) (2022-07-05)


### Bug Fixes

* package ([9b03de0](https://github.com/Romanchuk/angular-i18next/commit/9b03de01029432a679b84f0b535c8c0986a2ce49))



## [14.0.5-1](https://github.com/Romanchuk/angular-i18next/compare/v14.0.0...v14.0.5-1) (2022-07-05)


### Bug Fixes

* package ([9b03de0](https://github.com/Romanchuk/angular-i18next/commit/9b03de01029432a679b84f0b535c8c0986a2ce49))



## [14.0.5-0](https://github.com/Romanchuk/angular-i18next/compare/v14.0.0...v14.0.5-0) (2022-07-05)


### Bug Fixes

* package ([9b03de0](https://github.com/Romanchuk/angular-i18next/commit/9b03de01029432a679b84f0b535c8c0986a2ce49))



## [14.0.4-0](https://github.com/Romanchuk/angular-i18next/compare/v14.0.0...v14.0.4-0) (2022-07-05)


### Bug Fixes

* package ([9b03de0](https://github.com/Romanchuk/angular-i18next/commit/9b03de01029432a679b84f0b535c8c0986a2ce49))



## [14.0.3](https://github.com/Romanchuk/angular-i18next/compare/v14.0.0...v14.0.3) (2022-07-05)


### Bug Fixes

* package ([9b03de0](https://github.com/Romanchuk/angular-i18next/commit/9b03de01029432a679b84f0b535c8c0986a2ce49))



## [14.0.2](https://github.com/Romanchuk/angular-i18next/compare/v14.0.0...v14.0.2) (2022-07-05)



## [14.0.1](https://github.com/Romanchuk/angular-i18next/compare/v14.0.0...v14.0.1) (2022-07-05)



# [14.0.0](https://github.com/Romanchuk/angular-i18next/compare/v14.0.0-0...v14.0.0) (2022-07-04)



# [14.0.0-0](https://github.com/Romanchuk/angular-i18next/compare/v11.0.0...v14.0.0-0) (2022-06-14)


### Bug Fixes

* [#81](https://github.com/Romanchuk/angular-i18next/issues/81) ([820a9e8](https://github.com/Romanchuk/angular-i18next/commit/820a9e8ab0d1b3f0c4757c4f7096dce4e1f844ed))



# [11.0.0](https://github.com/Romanchuk/angular-i18next/compare/v11.0.0-0...v11.0.0) (2022-01-28)



# [11.0.0-0](https://github.com/Romanchuk/angular-i18next/compare/v10.3.0...v11.0.0-0) (2022-01-04)


### Bug Fixes

* np dist ([670004f](https://github.com/Romanchuk/angular-i18next/commit/670004f2b1c41e0de88708767563b960bd88a3e6))



# [10.3.0](https://github.com/Romanchuk/angular-i18next/compare/v10.3.0-0...v10.3.0) (2021-06-15)



# [10.3.0-0](https://github.com/Romanchuk/angular-i18next/compare/v10.2.0...v10.3.0-0) (2021-06-15)



# [10.2.0](https://github.com/Romanchuk/angular-i18next/compare/v10.2.0-0...v10.2.0) (2021-05-12)



# [10.2.0-0](https://github.com/Romanchuk/angular-i18next/compare/v10.1.0...v10.2.0-0) (2021-05-12)


### Features

* i18next v20+ support ([0327a7c](https://github.com/Romanchuk/angular-i18next/commit/0327a7c9f35140f0c8e098d9d1528b6e7303a8d0))



# [10.1.0](https://github.com/Romanchuk/angular-i18next/compare/v10.1.0-0...v10.1.0) (2021-03-01)



# [10.1.0-0](https://github.com/Romanchuk/angular-i18next/compare/v10.0.1...v10.1.0-0) (2021-03-01)


### Bug Fixes

* **I18NextEagerPipe:** ensure changing PipeOptions returns correct translated value a not cached one with different PipeOptions but same key ([4a6d375](https://github.com/Romanchuk/angular-i18next/commit/4a6d375181dda41399c58f7644b97d3755acf84f))



## [10.0.1](https://github.com/Romanchuk/angular-i18next/compare/v10.0.1-beta...v10.0.1) (2020-12-21)



## [10.0.1-beta](https://github.com/Romanchuk/angular-i18next/compare/v10.0.0...v10.0.1-beta) (2020-12-21)



# [10.0.0](https://github.com/Romanchuk/angular-i18next/compare/v10.0.0-2...v10.0.0) (2020-07-06)



# [10.0.0-2](https://github.com/Romanchuk/angular-i18next/compare/v10.0.0-1...v10.0.0-2) (2020-07-06)



# [10.0.0-1](https://github.com/Romanchuk/angular-i18next/compare/v10.0.0-0...v10.0.0-1) (2020-07-06)



# [10.0.0-0](https://github.com/Romanchuk/angular-i18next/compare/v9.0.1...v10.0.0-0) (2020-07-06)



## [9.0.1](https://github.com/Romanchuk/angular-i18next/compare/v9.0.0...v9.0.1) (2020-02-25)


### Bug Fixes

* pass translate options ([4cfe42c](https://github.com/Romanchuk/angular-i18next/commit/4cfe42c))



# [9.0.0](https://github.com/Romanchuk/angular-i18next/compare/v8.1.0-beta.3...v9.0.0) (2020-02-20)



# [8.1.0-beta.3](https://github.com/Romanchuk/angular-i18next/compare/v8.1.0-beta.2...v8.1.0-beta.3) (2020-02-20)



# [8.1.0-beta.2](https://github.com/Romanchuk/angular-i18next/compare/v8.1.0-beta.1...v8.1.0-beta.2) (2020-02-20)



# [8.1.0-beta.1](https://github.com/Romanchuk/angular-i18next/compare/v8.1.0-beta...v8.1.0-beta.1) (2020-02-20)


### Features

* improved typings ([214e35d](https://github.com/Romanchuk/angular-i18next/commit/214e35d))



# [8.1.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v7.2.0-beta...v8.1.0-beta) (2020-02-20)



## [8.0.1](https://github.com/Romanchuk/angular-i18next/compare/v8.0.1-beta.0...v8.0.1) (2020-02-18)



## [8.0.1-beta.0](https://github.com/Romanchuk/angular-i18next/compare/v8.0.1-beta...v8.0.1-beta.0) (2020-02-18)



## [8.0.1-beta](https://github.com/Romanchuk/angular-i18next/compare/v8.0.0...v8.0.1-beta) (2020-02-18)



# [8.0.0](https://github.com/Romanchuk/angular-i18next/compare/v8.0.0-beta.1...v8.0.0) (2020-02-14)



# [8.0.0-beta.1](https://github.com/Romanchuk/angular-i18next/compare/v8.0.0-beta...v8.0.0-beta.1) (2020-02-13)



# [8.0.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v7.2.0-beta...v8.0.0-beta) (2020-02-13)



# [7.2.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v7.0.0...v7.2.0-beta) (2020-01-28)


### Bug Fixes

* I18NextEagerPipe ([8dbefe1](https://github.com/Romanchuk/angular-i18next/commit/8dbefe1))



# [7.0.0](https://github.com/Romanchuk/angular-i18next/compare/v6.1.0...v7.0.0) (2019-06-05)



# [6.1.0](https://github.com/Romanchuk/angular-i18next/compare/v6.1.0-beta...v6.1.0) (2019-05-27)



# [6.1.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v6.0.1...v6.1.0-beta) (2019-05-25)



## [6.0.1](https://github.com/Romanchuk/angular-i18next/compare/v6.0.0...v6.0.1) (2019-03-11)



# [6.0.0](https://github.com/Romanchuk/angular-i18next/compare/v6.0.0-beta.0...v6.0.0) (2019-02-10)



# [6.0.0-beta.0](https://github.com/Romanchuk/angular-i18next/compare/v6.0.0-beta...v6.0.0-beta.0) (2019-02-10)



# [6.0.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v5.0.6...v6.0.0-beta) (2019-02-10)



## [5.0.6](https://github.com/Romanchuk/angular-i18next/compare/v5.0.5...v5.0.6) (2018-12-03)



## [5.0.5](https://github.com/Romanchuk/angular-i18next/compare/v5.0.4...v5.0.5) (2018-12-03)



## [5.0.4](https://github.com/Romanchuk/angular-i18next/compare/v5.0.3...v5.0.4) (2018-12-03)



## [5.0.3](https://github.com/Romanchuk/angular-i18next/compare/v5.0.2...v5.0.3) (2018-12-03)



## [5.0.2](https://github.com/Romanchuk/angular-i18next/compare/v5.0.1...v5.0.2) (2018-12-03)


### Bug Fixes

* package.json ([54a8c37](https://github.com/Romanchuk/angular-i18next/commit/54a8c37))



## [5.0.1](https://github.com/Romanchuk/angular-i18next/compare/v5.0.0...v5.0.1) (2018-11-28)



# [5.0.0](https://github.com/Romanchuk/angular-i18next/compare/v5.0.0-beta2...v5.0.0) (2018-11-28)



# [5.0.0-beta2](https://github.com/Romanchuk/angular-i18next/compare/v5.0.0-beta...v5.0.0-beta2) (2018-11-28)



# [5.0.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v4.0.0...v5.0.0-beta) (2018-11-28)


### Bug Fixes

* docs ([220a0b8](https://github.com/Romanchuk/angular-i18next/commit/220a0b8))



<a name="4.0.0"></a>
# [4.0.0](https://github.com/Romanchuk/angular-i18next/compare/v4.0.0-beta...v4.0.0) (2018-06-25)

In v4 passed through most of i18next api methods

1. Update angular to v6+
2. Update rxjs to v6.2.0+


<a name="4.0.0-beta"></a>
# [4.0.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v3.4.2...v4.0.0-beta) (2018-06-11)



<a name="3.4.2"></a>
## [3.4.2](https://github.com/Romanchuk/angular-i18next/compare/v3.4.1...v3.4.2) (2018-05-05)



<a name="3.4.1"></a>
## [3.4.1](https://github.com/Romanchuk/angular-i18next/compare/v3.4.0...v3.4.1) (2018-04-29)

- default formater fixes


<a name="3.4.0"></a>
# [3.4.0](https://github.com/Romanchuk/angular-i18next/compare/v3.3.0...v3.4.0) (2018-04-29)

- i18next v11 support 
- fix: [format pipe](https://github.com/Romanchuk/angular-i18next/issues/15)


<a name="3.3.0"></a>
# [3.3.0](https://github.com/Romanchuk/angular-i18next/compare/v3.3.0-beta.2...v3.3.0) (2018-03-12)

- added umd bundle
- comments cleanup
- updated dev dependencies

<a name="3.3.0-beta.2"></a>
# [3.3.0-beta.2](https://github.com/Romanchuk/angular-i18next/compare/v3.3.0-beta.1...v3.3.0-beta.2) (2018-03-12)



<a name="3.3.0-beta.1"></a>
# [3.3.0-beta.1](https://github.com/Romanchuk/angular-i18next/compare/v3.2.0...v3.3.0-beta.1) (2018-02-04)



<a name="3.2.0"></a>
# [3.2.0](https://github.com/Romanchuk/angular-i18next/compare/v3.1.1...v3.2.0) (2018-01-17)

### Bug Fixes
* [aot build failed](Romanchuk/angular-i18next#10)

### Breaking changes

Removed parameter 'localizeTitle' from forRoot method.
You need to manually resolve Title as I18NextTitle for same behavior.


<a name="3.1.1"></a>
## [3.1.1](https://github.com/Romanchuk/angular-i18next/compare/v3.1.0...v3.1.1) (2018-01-01)


### Bug Fixes

* bug namespace fallback ([a16b067](https://github.com/Romanchuk/angular-i18next/commit/a16b067))
* conventional-github-releaser run ([df3bb84](https://github.com/Romanchuk/angular-i18next/commit/df3bb84))



<a name="3.1.0"></a>
# [3.1.0](https://github.com/Romanchuk/angular-i18next/compare/v3.0.0...v3.1.0) (2017-12-22)

It is possible to pass array of namespaces (or scopes). [Key would fallback](https://www.i18next.com/api.html#t) to next namespace in array if the previous failed to resolve.

`[feature.validators:key, validators:key]`
```typescript
{
  provide: I18NEXT_NAMESPACE,
  useValue: ['feature.validators', 'validators']
}
```

<a name="3.0.0"></a>
# [3.0.0](https://github.com/Romanchuk/angular-i18next/compare/v3.0.0-alpha.2...v3.0.0) (2017-12-15)



<a name="3.0.0-alpha.2"></a>
# [3.0.0-alpha.2](https://github.com/Romanchuk/angular-i18next/compare/v3.0.0-alpha...v3.0.0-alpha.2) (2017-12-05)



<a name="3.0.0-alpha"></a>
# [3.0.0-alpha](https://github.com/Romanchuk/angular-i18next/compare/v2.0.0...v3.0.0-alpha) (2017-11-27)



<a name="2.0.0"></a>
# [2.0.0](https://github.com/Romanchuk/angular-i18next/compare/v2.0.0-beta2...v2.0.0) (2017-11-14)



<a name="2.0.0-beta2"></a>
# [2.0.0-beta2](https://github.com/Romanchuk/angular-i18next/compare/v2.0.0-beta...v2.0.0-beta2) (2017-11-05)



<a name="2.0.0-beta"></a>
# [2.0.0-beta](https://github.com/Romanchuk/angular-i18next/compare/v1.1.0...v2.0.0-beta) (2017-11-05)



<a name="1.1.0"></a>
# [1.1.0](https://github.com/Romanchuk/angular-i18next/compare/v1.0.2...v1.1.0) (2017-11-04)



<a name="1.0.2"></a>
## [1.0.2](https://github.com/Romanchuk/angular-i18next/compare/v1.0.1...v1.0.2) (2017-09-22)



<a name="1.0.1"></a>
## [1.0.1](https://github.com/Romanchuk/angular-i18next/compare/v1.0.0...v1.0.1) (2017-09-21)



<a name="1.0.0"></a>
# [1.0.0](https://github.com/Romanchuk/angular-i18next/compare/v0.2.4...v1.0.0) (2017-09-21)



<a name="0.2.4"></a>
## [0.2.4](https://github.com/Romanchuk/angular-i18next/compare/v0.2.3...v0.2.4) (2017-06-29)



<a name="0.2.3"></a>
## [0.2.3](https://github.com/Romanchuk/angular-i18next/compare/v0.2.2...v0.2.3) (2017-06-29)



<a name="0.2.2"></a>
## [0.2.2](https://github.com/Romanchuk/angular-i18next/compare/v0.2.1...v0.2.2) (2017-06-29)


### Bug Fixes

* **I18NextService:** context-safe calls of i18next methods ([455a07d](https://github.com/Romanchuk/angular-i18next/commit/455a07d))



<a name="0.2.1"></a>
## [0.2.1](https://github.com/Romanchuk/angular-i18next/compare/v0.2.0...v0.2.1) (2017-06-29)


### Bug Fixes

* **package:** return back required exports ([fb7ead6](https://github.com/Romanchuk/angular-i18next/commit/fb7ead6))



<a name="0.2.0"></a>
# [0.2.0](https://github.com/Romanchuk/angular-i18next/compare/0.1.0...0.2.0) (2017-06-28)


### Features

* **package:** AOT support added ([fc1f66d](https://github.com/Romanchuk/angular-i18next/commit/fc1f66d))





================================================
FILE: libs/angular-i18next/README.md
================================================
[![npm version](https://badge.fury.io/js/angular-i18next.svg)](https://badge.fury.io/js/angular-i18next)
[![Downloads](http://img.shields.io/npm/dm/angular-i18next.svg)](https://npmjs.org/package/angular-i18next)
[![Build Status](https://travis-ci.com/Romanchuk/angular-i18next.svg?branch=master)](https://travis-ci.com/Romanchuk/angular-i18next)
[![Coverage Status](https://coveralls.io/repos/github/Romanchuk/angular-i18next/badge.svg?branch=master)](https://coveralls.io/github/Romanchuk/angular-i18next?branch=master)
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
[![Dependency Status](https://david-dm.org/Romanchuk/angular-i18next.svg)](https://david-dm.org/Romanchuk/angular-i18next)
[![devDependency Status](https://david-dm.org/Romanchuk/angular-i18next/dev-status.svg)](https://david-dm.org/Romanchuk/angular-i18next?type=dev)
[![paypal](https://img.shields.io/badge/say_thanks-%2410-green)](https://www.paypal.com/paypalme2/sergeyromanchuk/10USD)
[![GitHub stars](https://img.shields.io/github/stars/romanchuk/angular-i18next?label=Please%20star%20repo%21&style=social)](https://github.com/romanchuk/angular-i18next)

# angular-i18next
[i18next](http://i18next.com/) v8.4+ integration with [angular](https://angular.io/) v2.0+

[Live DEMO](https://romanchuk.github.io/angular-i18next-demo/)

 - [Features](#features)
 - [Installation](#installation)
 - [Usage](#usage)
 - [Cookbook](#cookbook)
 - [Deep integration](#deep-integration)
 - [In-project testing](#in-project-testing)
 - [Demo](#demo)
 - [Articles](#articles)
 - [Support project](#support-on-beerpay)
 

# Features

- Native i18next [options](https://www.i18next.com/configuration-options.html)
- Promise initialization
- [i18next plugin](https://www.i18next.com/plugins-and-utils.html#plugins) support 
- Events support
- Namespaces lazy load
- i18next native [format](https://www.i18next.com/api.html#format) support
- document.title localization
- Error handling strategies
- i18next namespaces and scopes (prefixes) for angular modules and components
- AOT support
- [Angular Package Format](https://docs.google.com/document/d/1CZC2rcpxffTDfRDs6p1cfbmKNLA6x5O-NtkJglDaBVs/preview) support

[Related packages](#deep-integration) also has implementations for:
- Reactive forms validators localization
- Http error message localizer

# Cheers!
Hey dude! Help me out for a couple of :beers:!

Поддержи проект - угости автора кружечкой пива!

[![paypal](https://img.shields.io/badge/paypal-%2410-green)](https://www.paypal.com/paypalme2/sergeyromanchuk/10USD)


# Installation

**1.** Install package

   ```
    npm install i18next --save
    npm install angular-i18next --save
  ```

**2.** Import I18NextModule to AppModule

```typescript

import { I18NextModule } from 'angular-i18next';

@NgModule({
  bootstrap: [ AppComponent ],
  declarations: [   
    AppComponent
  ],
  import: [
    I18NextModule.forRoot()
  ]
})
export class AppModule {}

```
**3.** Import I18NextModule.forRoot() to AppModule and setup provider with "init" method (use native [options](https://www.i18next.com/configuration-options.html)). Angular would not load until i18next initialize event fired
```typescript
export function appInit(i18next: ITranslationService) {
    return () => i18next.init({
        whitelist: ['en', 'ru'],
        fallbackLng: 'en',
        debug: true,
        returnEmptyString: false,
        ns: [
          'translation',
          'validation',
          'error'          
        ],
      });
}

export function localeIdFactory(i18next: ITranslationService)  {
    return i18next.language;
}

export const I18N_PROVIDERS = [
{
    provide: APP_INITIALIZER,
    useFactory: appInit,
    deps: [I18NEXT_SERVICE],
    multi: true
},
{
    provide: LOCALE_ID,
    deps: [I18NEXT_SERVICE],
    useFactory: localeIdFactory
}];
```

```typescript
@NgModule({
    imports: [
        ...
        I18NextModule.forRoot()
    ],
    providers: [
        ...
        I18N_PROVIDERS, 
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}
```

# Usage

### Pipes

Use "i18next" pipe to translate key:

    <div>{{ 'test' | i18next }}</div>

Passing ["t options"](https://www.i18next.com/api.html#t):

    <div>{{ 'test' | i18next: { count: 5, nsSeparator: '#' } }}</div>


Trigger native i18next [format method](https://www.i18next.com/formatting.html) by using I18NextFormatPipe or I18NextPipe with option 'format':

`{{ 'any_key' | i18next | i18nextFormat }}`

`{{ 'any_key' | i18next: { format: 'cap' } }}`

`{{ 'any_key' | i18nextCap }}`

**Note:** Using "i18nextCap" you will get the same result as  `i18next: { format: 'cap' }`

**REMEMBER** that format will not work until you set "interpolation.format" function in i18next options.

I18NextModule has static method `static interpolationFormat(customFormat: Function = null): Function` that can be used as default interpolation format function (it provides 'upper', 'cap' and 'lower' formatters). You also can pass your custom function to be called after I18NextModule formatters:

```typescript
const i18nextOptions = {
  whitelist: ['en', 'ru'],
  ns: [
    'translation',
    'validation',
    'error',
  ],
  interpolation: {
    format: I18NextModule.interpolationFormat((value, format, lng) => {
      if(value instanceof Date)
        return moment(value).format(format);
      return value;
    });
    // format: I18NextModule.interpolationFormat()
  }
};

```

**i18nextEager pipe**

This is the impure analog of *i18next pipe* that is subscribed to language change, it will change string right away to choosen language (without reloading page).

**Warning!**: Use i18nextEager only in combine with [OnPush change detection strategy](https://netbasal.com/a-comprehensive-guide-to-angular-onpush-change-detection-strategy-5bac493074a4), or else (default change detection) each pipe will retrigger more than one time (cause of performance issues).

Subscribing to event observables:
```typescript
this.i18NextService.events.languageChanged.subscribe(lang => {
  // do something
})
```

Add a provider to module/component if you want to prefix child i18next keys:
```typescript
{
  provide: I18NEXT_NAMESPACE,
  useValue: 'feature' // set 'feature:' prefix 
}
```
```typescript
{
  provide: I18NEXT_SCOPE,
  useValue: 'person' // set 'person.' prefix 
}
```
Since v3.1.0+ it is possible to pass array of namespaces (or scopes). [Key would fallback](https://www.i18next.com/api.html#t) to next namespace in array if the previous failed to resolve.

`[feature_validators:key, validators:key]`
```typescript
{
  provide: I18NEXT_NAMESPACE,
  useValue: ['feature_validators', 'validators']
}
```
_NOTE:_ **Do NOT** use default (or custom) i18next delimiters in namespace names.

### Document title
If you want to turn on document title localization resolve Title as `I18NextTitle` imported from 'angular-i18next':

```typescript
{
  provide: Title,
  useClass: I18NextTitle
}
```

Also you can implement your own Title service with specific behavior. Inject `I18NextPipe` (or `I18NextService`) to service/component:
```typescript
import { Injectable, Inject } from '@angular/core';
import { Title, DOCUMENT } from '@angular/platform-browser';
import { I18NextPipe } from 'angular-i18next';

@Injectable()
export class I18NextTitle extends Title {
   constructor(private i18nextPipe: I18NextPipe, @Inject(DOCUMENT) doc) {
    super(doc);
   }

   setTitle(value: string) {
    return super.setTitle(this.translate(value));
   }

   private translate(text: string) {
     return this.i18nextPipe.transform(text, { format: 'cap'});
   }
}

```

Ways to use I18NextService in your code:
> **Warning:** Injection of **I18NextService** is possible, but it would not consider I18NEXT_NAMESPACE and I18NEXT_SCOPE providers. There are 2 possible reasons to inject **I18NextService**: initialization and subscription to its events. In all other cases inject **I18NextPipe**.
1) **Recommended way:** Inject via **I18NEXT_SERVICE** token. By default it will inject instance of **I18NextService**.
```typescript
export class AppComponent implements OnInit  {
  constructor(private router: Router,
              private title: Title,
              @Inject(I18NEXT_SERVICE) private i18NextService: ITranslationService) 
```

2) Legacy way: Inject via type
```typescript
export class AppComponent implements OnInit  {
  constructor(private router: Router,
              private title: Title,
              private i18NextService: I18NextService) 
```

### Error handling

Error handling is now configurable:
  1) By default i18next promise will use NativeErrorHandlingStrategy. I18Next would be always resolve succesfully. Error could be get from 'then' handler parameter.
  2) Set StrictErrorHandlingStrategy to reject load promises (init, languageChange, loadNamespaces) on first load fail (this was default in v2 but changed to fit [native i18next behavior](https://github.com/Romanchuk/angular-i18next/issues/9):

    `I18NextModule.forRoot({ errorHandlingStrategy: StrictErrorHandlingStrategy })`

    

### Lazy loading

Use I18NEXT_NAMESPACE_RESOLVER in your routes to to load i18next namespace.

Note: It is not neccesary to register lazy loading namespaces in global i18next options.

```
{
    path: 'rich_form',
    loadChildren: 'app/features/rich_form_feature/RichFormFeatureModule#RichFormFeatureModule',
    data: {
      i18nextNamespaces: ['feature.rich_form']
    },
    resolve: {
      i18next: I18NEXT_NAMESPACE_RESOLVER
    }
 },

```
Use I18NextService.loadNamespaces() method to load namespaces in code.


# Cookbook

### i18next plugin support

```typescript
import { I18NextModule, ITranslationService, I18NEXT_SERVICE } from 'angular-i18next';
//  import Backend from 'i18next-xhr-backend'; //for i18next < 20.0.0
import HttpApi from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';

...

i18next.use(HttpApi)
       .use(LanguageDetector)
       .init(i18nextOptions)
```




### Initialize i18next before angular application
Angular would not load until i18next initialize event fired
```typescript
export function appInit(i18next: ITranslationService) {
    return () => i18next.init();
}

export function localeIdFactory(i18next: ITranslationService)  {
    return i18next.language;
}

export const I18N_PROVIDERS = [
{
    provide: APP_INITIALIZER,
    useFactory: appInit,
    deps: [I18NEXT_SERVICE],
    multi: true
},
{
    provide: LOCALE_ID,
    deps: [I18NEXT_SERVICE],
    useFactory: localeIdFactory
}];
```



### Document title update on language or route change


```typescript
export class AppComponent implements OnInit  {
  constructor(private router: Router,
              private title: Title,
              @Inject(I18NEXT_SERVICE) private i18NextService: ITranslationService) {
      // page title subscription
      // https://toddmotto.com/dynamic-page-titles-angular-2-router-events#final-code
      this.router.events
        .filter(event => event instanceof NavigationEnd)
        .map(() => this.router.routerState.root)
        .map(route => {
          while (route.firstChild) route = route.firstChild;
          return route;
        })
        .filter(route => route.outlet === 'primary')
        .mergeMap(route => route.data)
        .subscribe((event) => this.updatePageTitle(event['title']));
  }

  ngOnInit() {
    this.i18NextService.events.languageChanged.subscribe(lang => {
      let root = this.router.routerState.root;
      if (root != null && root.firstChild != null) {
        let data: any = root.firstChild.data;
        this.updatePageTitle(data && data.value && data.value.title);
      }
    });
  }

  updatePageTitle(title: string): void {
    let newTitle = title || 'application_title';
    this.title.setTitle(newTitle);
  }
}
```
Routes example:
```typescript
const appRoutes: Routes = [
  { 
    path: 'error',
    component: AppErrorComponent,
    data: { title: 'error:error_occured' }
  },
  { 
    path: 'denied',
    component: AccessDeniedComponent,
    data: { title: 'error:access_denied' }
  }
];
```

# New angular version released, but angular-i18next is not released YET!!!

Angular releases mostly don't break angular-i18next, but we cannot tell ahead that current version of `angular-i18next` will work correctly with latest angular version.

You can override an angular-i18next `peerDependencies` in your `package.json` on your **own risk**:

```json
"overrides": {
  "angular-i18next": {
    "@angular/common": "*",
    "@angular/core": "*",
    "@angular/platform-browser": "*"
  }
}
```

# Deep integration

List of packages to integrate angular and i18next more deeply:

- [angular-validation-message](https://github.com/Romanchuk/angular-validation-message) - angular [reactive form validators](https://angular.io/guide/reactive-forms#step-2-making-a-field-required) integration (and [angular-validation-message-i18next ](https://github.com/Romanchuk/angular-validation-message-i18next) is i18next bridge to it). It gives you possibility to localize form validators and it automatically puts localized validator error message to markup (if there is one).
- [angular-i18next-error-interceptor](https://github.com/LCGroupIT/angular-i18next-error-interceptor) - allows you to set default errot messages for non-200 http status responses. So if the back-end didn't specify { message: 'some error' } in a response (sort of contract with our backend) interceptor will check response status code and will fill { message: 'Server is not available. Please try again.' }. Also package includes pipe where you can pass HttpErrorResponse and it will return error message whenever it's back-end message or our localized message.

# In-project testing

You might want to unit-test project components that are using i18next pipes

Example tests setup:
[/tests/projectTests/projectTests.spec.ts](https://github.com/Romanchuk/angular-i18next/blob/master/tests/projectTests/projectTests.spec.ts)

# Demo

[Live DEMO](https://romanchuk.github.io/angular-i18next-demo/)
Demo app source code available here: https://github.com/Romanchuk/angular-i18next-demo


# Articles
- [Angular L10n with I18next](https://phrase.com/blog/posts/angular-l10n-with-i18next/)
- [Best Libraries for Angular I18n](https://phrase.com/blog/posts/best-libraries-for-angular-i18n/)



================================================
FILE: libs/angular-i18next/forms/ng-package.json
================================================
{}


================================================
FILE: libs/angular-i18next/forms/src/components/validation-message.component.ts
================================================
import { Component, ViewEncapsulation, computed, effect, inject, input, signal } from "@angular/core";
import { AbstractControl, NgControl } from "@angular/forms";
import { I18NEXT_NAMESPACE, I18NextCapPipe } from "angular-i18next";
import { ValidationMessage } from "../models";
import { combineLatest, startWith, Subscription, tap } from "rxjs";

@Component({
  selector: 'i18next-validation-message',
  template: `
    <div class="error-container">{{ i18nextKey() | i18nextCap: firstMessage().params }}</div>
    <i class="error-icon"></i>
  `,
  styles: [`
    .i18next-validation-message {
      display: none;
      width: 100%;
      position: relative;
    }
    .i18next-validation-message.standalone,
    .ng-dirty.ng-invalid + .i18next-validation-message {
      display: block;
    }
  `],
  encapsulation: ViewEncapsulation.None,
  standalone: true,
  imports: [I18NextCapPipe],
  host: {
    'class': 'i18next-validation-message'
  }
})
export class I18NextValidationMessageComponent {
  private readonly i18nextNamespace = inject<string | string[]>(I18NEXT_NAMESPACE);
  private readonly validationString = 'validation';
  private readonly manualSettedFor = signal<NgControl | null>(null);
  private readonly messages = signal<ValidationMessage[]>([]);
  private controlChangesSub: Subscription | null = null;

  for = input<NgControl | null>(null);

  setFor(control: NgControl) {
    this.manualSettedFor.set(control);
  }

  constructor() {
    effect(() => {
        this.controlChangesSub?.unsubscribe();
        this.messages.set([]);
        const control = this.control();
        if (!control?.valueChanges) {
          return;
        }
        control.statusChanges?.pipe(tap((s) => console.log(s))).subscribe();
        this.controlChangesSub = combineLatest([control.valueChanges, control.statusChanges]).pipe(
          startWith([control.value, control.status]),
          tap(() => {
             this.messages.set(this.getErrorMessages(control))
          })
        ).subscribe();
    });
  }

  protected readonly control = computed(() => this.for() ?? this.manualSettedFor());

  protected readonly firstMessage = computed(() =>
    this.messages()[0] ?? new ValidationMessage()
  );

  protected readonly controlPath = computed(() =>
    this.control()?.path?.join('.') ?? ''
  );

  protected readonly i18nextKey = computed(() => {
    if (!this.firstMessage().key) return '';

    const specificKey = [
      this.validationString,
      ['control_specific', this.controlPath(), this.firstMessage().key].join('.')
    ].join(':');

    const commonKey = [this.validationString, this.firstMessage().key].join(':');
    const i18nextKeys: string[] = [];

    if (this.i18nextNamespace && this.i18nextNamespace !== this.validationString) {
      i18nextKeys.push([this.i18nextNamespace, specificKey].join('.'));
      i18nextKeys.push([this.i18nextNamespace, commonKey].join('.'));
    }

    i18nextKeys.push(specificKey);
    i18nextKeys.push(commonKey);

    return i18nextKeys;
  });

  private getErrorMessages(control: NgControl) {
    const errors = control.errors;
    if (!errors) return [];

    return Object.entries(errors ?? {}).map(([key, value]) => {
      let params = null;
      if (value instanceof Object) {
        params = value;
      } else if (value !== true) {
        params = { [key]: value };
      }
      return new ValidationMessage(key, params);
    });
  }
}


================================================
FILE: libs/angular-i18next/forms/src/directives/validation-message.directive.ts
================================================
import { AfterViewInit, ComponentRef, Directive, inject, OnDestroy, ViewContainerRef } from "@angular/core";
import { FormControlName, NgControl } from "@angular/forms";
import { I18NextValidationMessageComponent } from "../components/validation-message.component";

@Directive({
  selector: '[formControlName][i18nextValidationMessage],[formGroupName][i18nextValidationMessage],[formArrayName][i18nextValidationMessage]',
  standalone: true
})
export class I18NextValidationMessageDirective implements AfterViewInit, OnDestroy {
  private readonly viewContainer = inject(ViewContainerRef);
  private readonly formControlName = inject(FormControlName, { optional: true });

  private validationMessageComponent: ComponentRef<I18NextValidationMessageComponent> | null = null;

  ngAfterViewInit(): void {
    this.detach();
    this.validationMessageComponent = this.viewContainer.createComponent(I18NextValidationMessageComponent);

    const control: NgControl = this.formControlName!;
    this.validationMessageComponent.instance.setFor(control);
    this.validationMessageComponent.changeDetectorRef.detectChanges();
  }

  ngOnDestroy(): void {
    this.detach();
  }

  private detach(): void {
    if (this.validationMessageComponent?.changeDetectorRef) {
      this.validationMessageComponent.changeDetectorRef.detach();
      this.validationMessageComponent = null;
    }
  }
}


================================================
FILE: libs/angular-i18next/forms/src/models.ts
================================================
/**
 * Represents a validation message with translation key and parameters
 */
export interface ValidationMessageParams {
  [key: string]: unknown;
}

export class ValidationMessage {
  constructor(
    public readonly key = '',
    public readonly params?: ValidationMessageParams
  ) {}
}


================================================
FILE: libs/angular-i18next/forms/src/public_api.ts
================================================
export { ValidationMessage, ValidationMessageParams } from './models';
export { I18NextValidationMessageComponent } from './components/validation-message.component';
export { I18NextValidationMessageDirective } from './directives/validation-message.directive';


================================================
FILE: libs/angular-i18next/jest.config.ts
================================================
/* eslint-disable */
export default {
  displayName: 'angular-i18next',
  preset: '../../jest.preset.js',
  setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
  globals: {},
  coverageDirectory: '../../libs/angular-i18next/coverage',
  transform: {
    '^.+\\.(ts|mjs|js|html)$': [
      'jest-preset-angular',
      {
        tsconfig: '<rootDir>/tsconfig.spec.json',
        stringifyContentPathRegex: '\\.(html|svg)$',
      },
    ],
  },
  transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
  snapshotSerializers: [
    'jest-preset-angular/build/serializers/no-ng-attributes',
    'jest-preset-angular/build/serializers/ng-snapshot',
    'jest-preset-angular/build/serializers/html-comment',
  ],
  useESM: true,
};


================================================
FILE: libs/angular-i18next/ng-package.json
================================================
{
  "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
  "assets": [
    "CHANGELOG.md",
    "postinstall.js"
  ],
  "lib": {
    "entryFile": "src/index.ts"
  }
}


================================================
FILE: libs/angular-i18next/package.json
================================================
{
  "name": "angular-i18next",
  "version": "21.0.0-3",
  "publishConfig": {
    "access": "public"
  },
  "np": {
    "contents": "./dist"
  },
  "author": {
    "name": "Sergey Romanchuk"
  },
  "homepage": "https://github.com/Romanchuk/angular-i18next#readme",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/Romanchuk/angular-i18next.git"
  },
  "engines": {
    "node": ">=22.0.0"
  },
  "license": "MIT",
  "description": "i18next module for Angular",
  "keywords": [
    "i18n",
    "i18next",
    "angular"
  ],
  "bugs": {
    "url": "https://github.com/Romanchuk/angular-i18next/issues"
  },
  "maintainers": [
    {
      "email": "rezety@gmail.com",
      "name": "Sergey Romanchuk"
    }
  ],
  "scripts": {
    "clean": "npm run clean:build && npm run clean:tests",
    "clean:build": "npx rimraf dist",
    "clean:tests": "npx rimraf coverage && npx rimraf tests/**/*.+{js,js.map,d.ts,metadata.json}",
    "copy:assets": "npx cpy CHANGELOG.md dist",
    "conventional-changelog": "npx conventional-changelog",
    "changelog": "npm run conventional-changelog -- -p angular -i CHANGELOG.md -s",
    "changelog:add": "git add --force CHANGELOG.md package.json",
    "changelog:commit": "git commit -m \"Updated CHANGELOG.md\"",
    "version": "npx conventional-changelog -p angular -i CHANGELOG.md -s -r && npx nx run angular-i18next:build && npm run copy:assets && npm run changelog:add && npm run changelog:commit",
    "release": "npx np",
    "test": "npx nx run angular-i18next:test"
  },
  "dependencies": {
    "tslib": "^2.8.1"
  },
  "peerDependencies": {
    "@angular/common": "^21.1.1",
    "@angular/core": "^21.1.1",
    "@angular/platform-browser": "^21.1.1",
    "i18next": "^25.4.0",
    "rxjs": "^7.8.2"
  },
  "optionalDependencies": {
    "@angular/ssr": "^21.1.1",
    "@angular/platform-server": "^21.1.1"
  }
}


================================================
FILE: libs/angular-i18next/postinstall.js
================================================
var BANNER = '\u001B[96mThank you for using angular-i18next (https://github.com/Romanchuk/angular-i18next). Please star the repo!\u001B[0m\n\n' +
             '\u001B[96mThe project needs your help! Please consider supporting of angular-i18next \u001B[0m\n' +
             '\u001B[96m>\u001B[94m Say thanks via donation: https://www.paypal.com/paypalme2/sergeyromanchuk/10USD \u001B[0m\n';

console.log(BANNER);


================================================
FILE: libs/angular-i18next/project.json
================================================
{
  "name": "angular-i18next",
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "projectType": "library",
  "sourceRoot": "libs/angular-i18next/src",
  "targets": {
    "build": {
      "dependsOn": ["clean"],
      "executor": "@nx/angular:package",
      "options": {
        "project": "libs/angular-i18next/ng-package.json",
        "tsConfig": "libs/angular-i18next/tsconfig.lib.json"
      }
    },
    "test": {
      "executor": "@nx/jest:jest",
      "outputs": ["{workspaceRoot}/coverage/libs/angular-i18next"],
      "options": {
        "jestConfig": "libs/angular-i18next/jest.config.ts",
        "passWithNoTests": true
      }
    },
    "lint": {
      "executor": "@nx/eslint:lint",
      "options": {
        "lintFilePatterns": [
          "libs/angular-i18next/src/**/*.ts",
          "libs/angular-i18next/src/**/*.html"
        ]
      }
    }
  },
  "tags": []
}


================================================
FILE: libs/angular-i18next/src/.eslintrc.json
================================================
{
  "extends": ["../../.eslintrc.json"],
  "ignorePatterns": ["!**/*"]
}


================================================
FILE: libs/angular-i18next/src/index.ts
================================================
export * from './lib/index';


================================================
FILE: libs/angular-i18next/src/lib/I18NextErrorHandlingStrategies.ts
================================================
import { I18NextLoadResult } from './I18NextLoadResult';
import * as i18n from 'i18next';

export interface I18NextErrorHandlingStrategy {
  handle(
    resolve: (thenableOrResult?: any) => void,
    reject: (error: any) => void
  ): i18n.Callback;
}

export class NativeErrorHandlingStrategy
  implements I18NextErrorHandlingStrategy
{
  handle(
    resolve: (thenableOrResult?: I18NextLoadResult) => void,
    reject: (error: any) => void
  ) {
    return (err: any, t?: Function) => {
      let result: I18NextLoadResult = {
        err: err,
        t: t,
      };
      resolve(result);
    };
  }
}

export class StrictErrorHandlingStrategy
  implements I18NextErrorHandlingStrategy
{
  handle(
    resolve: (thenableOrResult?: I18NextLoadResult) => void,
    reject: (error: any) => void
  ) {
    return (err: any, t?: any) => {
      let result: I18NextLoadResult = {
        err: err,
        t: t,
      };
      if (!err) {
        resolve(result);
        return;
      }
      reject(err);
    };
  }
}


================================================
FILE: libs/angular-i18next/src/lib/I18NextEvents.ts
================================================
import { BehaviorSubject, Subject } from 'rxjs';

import {
  ITranslationEvents,
  MissingKeyEvent,
  ResourceEvent,
} from './services/translation.events';
import * as i18n from 'i18next';

export class I18NextEvents implements ITranslationEvents {
  initialized = new BehaviorSubject<i18n.InitOptions | undefined>(undefined);
  loaded = new BehaviorSubject(false);
  failedLoading = new Subject();
  missingKey = new Subject<MissingKeyEvent>();
  added = new Subject<ResourceEvent>();
  removed = new Subject<ResourceEvent>();
  languageChanged = new BehaviorSubject<string | null>(null);
}


================================================
FILE: libs/angular-i18next/src/lib/I18NextLoadResult.ts
================================================
export interface I18NextLoadResult {
  err: any;
  t?: Function;
}


================================================
FILE: libs/angular-i18next/src/lib/I18NextModuleParams.ts
================================================
import { Type } from '@angular/core';
import { I18NextErrorHandlingStrategy } from './I18NextErrorHandlingStrategies';

export interface I18NextModuleParams {
  errorHandlingStrategy?: Type<I18NextErrorHandlingStrategy>;
}


================================================
FILE: libs/angular-i18next/src/lib/index.ts
================================================
export * from './I18NextErrorHandlingStrategies';
export * from './I18NextEvents';
export * from './I18NextLoadResult';
export * from './I18NextModuleParams';
export * from './interpolation';
export * from './models';
export * from './module';
export * from './namespaces.guard';
export * from './pipes/i18next-cap.pipe';
export * from './pipes/i18next-eager.pipe';
export * from './pipes/i18next-format.pipe';
export * from './pipes/i18next.pipe';
export * from './provider';
export * from './provider.utils';
export * from './services/i18next-title';
export * from './services/i18next.service';
export * from './services/translation.events';
export * from './services/translation.service';
export * from './tokens';



================================================
FILE: libs/angular-i18next/src/lib/interpolation.ts
================================================
import type { FormatFunction, InterpolationOptions } from "i18next";

export function defaultInterpolationFormat(
    value: any,
    format?: string,
    lng?: string
  ): string {
    if (!value) return value;
    switch (format) {
      case 'upper':
      case 'uppercase':
        return value.toUpperCase();
      case 'lower':
      case 'lowercase':
        return value.toLowerCase();
      case 'cap':
      case 'capitalize':
        return value.charAt(0).toUpperCase() + value.slice(1);
      case null:
      case undefined:
      case 'none':
      default:
        return value;
    }
  }

  export function interpolationFormat(customFormat: Function | null = null): FormatFunction {
    function formatDelegate(value: any,
                            format?: string,
                            lng?: string,
                            options?: InterpolationOptions & { [key: string]: any }
    ): string {
      let formatedValue: string = defaultInterpolationFormat(
        value,
        format,
        lng
      );
      if (customFormat === null) return formatedValue;
      return customFormat(formatedValue, format, lng);
    }
    return formatDelegate;
  }

================================================
FILE: libs/angular-i18next/src/lib/models.ts
================================================
import type * as i18n from 'i18next';

export type FormatPipeOptions = { format?: string; lng?: string; case?: string; [key: string]: any };
export type PrependPipeOptions = {
  prependScope?: boolean;
  prependNamespace?: boolean;
};


export type PipeOptions = i18n.TOptions &
  FormatPipeOptions &
  PrependPipeOptions;

export type NamespaceResolver = (
  activatedRouteSnapshot: any,
  routerStateSnapshot?: any
) => Promise<void>


================================================
FILE: libs/angular-i18next/src/lib/module.ts
================================================
import { NgModule, ModuleWithProviders } from "@angular/core";
import { FormatFunction, InterpolationOptions } from "i18next";
import { I18NEXT_NAMESPACE, I18NEXT_SCOPE, I18NEXT_INSTANCE, I18NEXT_SERVICE, I18NEXT_ERROR_HANDLING_STRATEGY, I18NEXT_NAMESPACE_RESOLVER } from "./tokens";
import { I18NextCapPipe } from "./pipes/i18next-cap.pipe";
import { I18NextEagerPipe } from "./pipes/i18next-eager.pipe";
import { I18NextErrorHandlingStrategy, NativeErrorHandlingStrategy } from "./I18NextErrorHandlingStrategies";
import { I18NextFormatPipe } from "./pipes/i18next-format.pipe";
import { I18NextModuleParams } from "./I18NextModuleParams";
import { I18NextPipe } from "./pipes/i18next.pipe";
import { I18NextService } from "./services/i18next.service";
import { I18NextTitle } from "./services/i18next-title";
import { defaultInterpolationFormat } from "./interpolation";
import * as i18n from 'i18next';
import { i18nextNamespaceResolverFactory } from "./namespace.resolver";

const i18nextGlobal: i18n.i18n = i18n.default;

/**
 * @deprecated Use provideI18Next() instead. This module-based approach will be removed in a future version.
 * Example:
 * ```typescript
 * // Instead of
 * imports: [I18NextModule.forRoot()]
 *
 * // Use
 * providers: [provideI18Next()]
 * ```
 */
@NgModule({
  imports: [I18NextPipe, I18NextEagerPipe, I18NextCapPipe, I18NextFormatPipe],
  exports: [I18NextPipe, I18NextEagerPipe, I18NextCapPipe, I18NextFormatPipe],
  providers: [
    {
      provide: I18NEXT_NAMESPACE,
      useValue: '',
    },
    {
      provide: I18NEXT_SCOPE,
      useValue: '',
    },
    I18NextTitle,
    I18NextFormatPipe
  ],
})
export class I18NextModule {
  /**
   * @deprecated Use provideI18Next() instead. This module-based approach will be removed in a future version.
   * Example:
   * ```typescript
   * // Instead of
   * imports: [I18NextModule.forRoot()]
   *
   * // Use
   * providers: [provideI18Next()]
   * ```
   */
  static forRoot(
    params: I18NextModuleParams = {}
  ): ModuleWithProviders<I18NextModule> {
    return {
      ngModule: I18NextModule,
      providers: [{
        provide: I18NEXT_INSTANCE,
        useValue: i18nextGlobal,
      },
        {
          provide: I18NEXT_SERVICE,
          useFactory: (errHandle: I18NextErrorHandlingStrategy, i18nextInstance: i18n.i18n) => new I18NextService(errHandle, i18nextInstance),
          deps: [
            I18NEXT_ERROR_HANDLING_STRATEGY,
            I18NEXT_INSTANCE
          ]
        },
        {
          provide: I18NEXT_ERROR_HANDLING_STRATEGY,
          useClass: params.errorHandlingStrategy || NativeErrorHandlingStrategy,
        },
        I18NextService,
        I18NextPipe,
        I18NextEagerPipe,
        I18NextCapPipe,
        I18NextFormatPipe,
        I18NextTitle,
        {
          provide: I18NEXT_NAMESPACE_RESOLVER,
          useFactory: i18nextNamespaceResolverFactory,
          deps: [I18NEXT_SERVICE],
        },
      ],
    };
  }

  static interpolationFormat(customFormat: Function | null = null): FormatFunction {
    function formatDelegate(value: any,
                            format?: string,
                            lng?: string,
                            options?: InterpolationOptions & { [key: string]: any }
    ): string {
      let formatedValue: string = defaultInterpolationFormat(
        value,
        format,
        lng
      );
      if (customFormat === null) return formatedValue;
      return customFormat(formatedValue, format, lng);
    }
    return formatDelegate;
  }
}


================================================
FILE: libs/angular-i18next/src/lib/namespace.resolver.ts
================================================
import { inject } from "@angular/core";
import { I18NEXT_SERVICE } from "./tokens";
import { ITranslationService } from "./services/translation.service";
import { NamespaceResolver } from "./models";

export function resolver(
  activatedRouteSnapshot: any,
  routerStateSnapshot: any
): NamespaceResolver {
  const i18next: ITranslationService = inject(I18NEXT_SERVICE);
  let namespaces: string[] = activatedRouteSnapshot.data?.i18nextNamespaces ?? [];
  // @ts-ignore
  return i18next.loadNamespaces(namespaces.filter((n) => n));
}

export function i18nextNamespaceResolverFactory() {
  return resolver;
}


================================================
FILE: libs/angular-i18next/src/lib/namespaces.guard.ts
================================================
import { inject } from "@angular/core";
import { I18NEXT_SERVICE } from "./tokens";

/**
 * This function can trigger the loading of I18Next namespaces and block route activation to ensure namespaces are loaded before navigation continues.
 *
 * @param i18nextNamespaces I18Next namespaces to load
 * @returns A functional guard that will load the I18Next Namespaces, and continue navigation when loaded.
 */
export const i18NextNamespacesGuard =
  (...i18nextNamespaces: string[]) =>
  () =>
    inject(I18NEXT_SERVICE)
      .loadNamespaces(i18nextNamespaces.filter(Boolean))
      .then(() => true)
      .catch(() => false);


================================================
FILE: libs/angular-i18next/src/lib/pipes/i18next-cap.pipe.ts
================================================
import { Inject, Injectable, Pipe, PipeTransform } from '@angular/core';
import {
  I18NEXT_NAMESPACE,
  I18NEXT_SCOPE,
  I18NEXT_SERVICE,
} from '../tokens';
import { I18NextPipe } from './i18next.pipe';
import { ITranslationService } from '../services/translation.service';
import { PipeOptions } from '../models';

@Injectable()
@Pipe({
  name: 'i18nextCap',
  standalone: true
})
export class I18NextCapPipe extends I18NextPipe implements PipeTransform {
  constructor(
    @Inject(I18NEXT_SERVICE) translateI18Next: ITranslationService,
    @Inject(I18NEXT_NAMESPACE) ns: string | string[],
    @Inject(I18NEXT_SCOPE) scope: string | string[]
  ) {
    super(translateI18Next, ns, scope);
  }

  public override transform(key: string | string[], options?: PipeOptions): string {
    options = options || {};
    options.format = 'cap';
    return super.transform(key, options);
  }
}


================================================
FILE: libs/angular-i18next/src/lib/pipes/i18next-eager.pipe.ts
================================================
import {
  ChangeDetectorRef,
  Inject,
  Pipe,
  PipeTransform
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { PipeOptions } from '../models';
import { ITranslationService } from '../services/translation.service';
import {
  I18NEXT_NAMESPACE,
  I18NEXT_SCOPE,
  I18NEXT_SERVICE,
} from '../tokens';
import { I18NextPipe } from './i18next.pipe';

@Pipe({
  name: 'i18nextEager',
  pure: false,
  standalone: true,
})
export class I18NextEagerPipe
  extends I18NextPipe
  implements PipeTransform
{
  private lastKey: string | undefined;
  private lastOptions: PipeOptions | undefined;
  private lastValue: string = '';

  constructor(
    @Inject(I18NEXT_SERVICE) protected override translateI18Next: ITranslationService,
    @Inject(I18NEXT_NAMESPACE) protected override ns: string | string[],
    @Inject(I18NEXT_SCOPE) protected override scope: string | string[],
    private cd: ChangeDetectorRef
  ) {
    super(translateI18Next, ns, scope);
    translateI18Next.events.languageChanged
      .pipe(takeUntilDestroyed())
      .subscribe(() => {
          this.cd.markForCheck();
      });
  }
  private hasKeyChanged(key: string | string[]): boolean {
    return !this.lastKey || this.lastKey !== key;
  }

  private hasOptionsChanged(options?: PipeOptions): boolean {
    return this.lastOptions !== options;
  }

  public override transform(key: string | string[], options?: PipeOptions): string {
    const newKey = this.translateI18Next.language + '|' + JSON.stringify(key);

    if (this.hasKeyChanged(newKey) || this.hasOptionsChanged(options)) {
      this.lastKey = newKey;
      this.lastOptions = options;
      this.lastValue = super.transform(key, options);
    }
    return this.lastValue;
  }
}


================================================
FILE: libs/angular-i18next/src/lib/pipes/i18next-format.pipe.ts
================================================
import { Inject, Injectable, Pipe, PipeTransform } from '@angular/core';
import { FormatPipeOptions } from '../models';
import { ITranslationService } from '../services/translation.service';
import { I18NEXT_SERVICE } from '../tokens';

@Injectable()
@Pipe({
  name: 'i18nextFormat',
  standalone: true
})
export class I18NextFormatPipe implements PipeTransform {
  constructor(
    @Inject(I18NEXT_SERVICE) private translateI18Next: ITranslationService
  ) {}

  public transform(value: any, options: FormatPipeOptions | string): string {
    let opts: FormatPipeOptions =
      typeof options === 'string' ? { format: options } : options;
    return this.translateI18Next.format(value, opts.format, opts.lng);
  }
}


================================================
FILE: libs/angular-i18next/src/lib/pipes/i18next.pipe.ts
================================================
import { Inject, Injectable, Pipe, PipeTransform } from '@angular/core';
import { PipeOptions } from '../models';
import { ITranslationService } from '../services/translation.service';
import {
  I18NEXT_NAMESPACE,
  I18NEXT_SCOPE,
  I18NEXT_SERVICE
} from '../tokens';

@Injectable()
@Pipe({
  name: 'i18next',
  standalone: true
})
export class I18NextPipe implements PipeTransform {
  constructor(
    @Inject(I18NEXT_SERVICE) protected translateI18Next: ITranslationService,
    @Inject(I18NEXT_NAMESPACE) protected ns: string | string[],
    @Inject(I18NEXT_SCOPE) protected scope: string | string[]
  ) {}

  public transform(key: string | string[], options?: PipeOptions): string {
    options = this.prepareOptions(options);

    let i18nOpts = this.translateI18Next.options;
    if (options.prependScope === undefined || options.prependScope === true) {
      if (this.scope) {
        key = this.prependScope(
          key,
          this.scope,
          i18nOpts.keySeparator,
          i18nOpts.nsSeparator
        );
      }
    }
    if (
      options.prependNamespace === undefined ||
      options.prependNamespace === true
    ) {
      if (this.ns) {
        key = this.prependNamespace(key, this.ns, i18nOpts.nsSeparator);
      }
    }

    let result = this.translateI18Next.t(key, options);

    if (options.format) {
      if (result) {
        result = this.translateI18Next.format(
          result,
          options.format,
          this.translateI18Next.language
        );
      }
    }
    return result ?? '';
  }

  private prependScope(
    key: string | string[],
    scope: string | string[],
    keySeparator: string | false | undefined,
    nsSeparator: string | false | undefined
  ): string[] {
    const nsSep = nsSeparator || '';
    const keySep = keySeparator || '';
    if (typeof key === 'string') {
      key = [key];
    }
    if (typeof scope === 'string') {
      scope = [scope];
    }
    let keysWithScope = [];
    for (let i = 0; i < key.length; i++) {
      const k = key[i];
      if (!this.keyContainsNsSeparator(k, nsSep)) {
        // Do not set scope, if key contains a namespace
        keysWithScope.push(
          ...scope.map((sc) => this.joinStrings(keySep, sc, k))
        );
      }
      keysWithScope.push(k);
    }
    return keysWithScope;
  }

  private prependNamespace(
    key: string | string[],
    ns: string | string[],
    nsSeparator: string | false | undefined
  ): string[] {
    const nsSep = nsSeparator || '';
    if (typeof key === 'string') {
      key = [key];
    }
    if (typeof ns === 'string') {
      ns = [ns];
    }
    let keysWithNamespace = [];
    for (let i = 0; i < key.length; i++) {
      const k = key[i];
      if (!this.keyContainsNsSeparator(k, nsSep)) {
        // Do not set namespace, if key contains a namespace
        keysWithNamespace.push(...ns.map((n) => this.joinStrings(nsSep, n, k)));
      }
      keysWithNamespace.push(k);
    }
    return keysWithNamespace;
  }

  private joinStrings(separator: string, ...str: string[]) {
    return [...str].join(separator);
  }

  private keyContainsNsSeparator(key: string, nsSeparator: string) {
    return key.indexOf(nsSeparator) !== -1;
  }

  private prepareOptions(options?: PipeOptions): PipeOptions {
    options = options || {};
    if (options.context != null) options.context = options.context.toString();
    return options;
  }
}


================================================
FILE: libs/angular-i18next/src/lib/provider.ts
================================================
import {
  ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID,
  EnvironmentProviders,
  inject,
  LOCALE_ID,
  makeEnvironmentProviders,
  Provider,
  Type
} from '@angular/core';
import { Title } from '@angular/platform-browser';
import * as i18n from 'i18next';
import {
  I18NextErrorHandlingStrategy,
  NativeErrorHandlingStrategy,
} from './I18NextErrorHandlingStrategies';
import { I18NextCapPipe } from './pipes/i18next-cap.pipe';
import { I18NextEagerPipe } from './pipes/i18next-eager.pipe';
import { I18NextFormatPipe } from './pipes/i18next-format.pipe';
import { I18NextPipe } from './pipes/i18next.pipe';
import { I18NextFeature, I18NextFeatureKind, makeI18NextFeature } from './provider.utils';
import { I18NextTitle } from './services/i18next-title';
import { I18NextService } from './services/i18next.service';
import {
  I18NEXT_ERROR_HANDLING_STRATEGY,
  I18NEXT_INSTANCE,
  I18NEXT_NAMESPACE,
  I18NEXT_SCOPE,
  I18NEXT_SERVICE
} from './tokens';

const i18nextGlobal: i18n.i18n = i18n.default;

export function localeIdFactory() {
  const i18next = inject(I18NEXT_SERVICE);
  return i18next.language ?? DEFAULT_LOCALE_ID;
}

  /**
   * Provides the necessary dependencies for using i18next with Angular.
   *
   * @param features An array of features to enable. See {@link I18NextFeature} for available features.
   * @returns An array of providers that can be added to the root providers.
   *
   * @example
   * import { provideI18Next } from '@angular-i18next/core';
   *
   *   providers: [
   *     provideI18Next(),
   *   ],
   *
   */
export function provideI18Next(
  ...features: I18NextFeature<I18NextFeatureKind>[]
): EnvironmentProviders {
  const providers: Provider[] = [
    {
      provide: I18NEXT_INSTANCE,
      useValue: i18nextGlobal,
    },
    {
      provide: I18NEXT_SERVICE,
      useFactory: (
        errHandle: I18NextErrorHandlingStrategy,
        i18nextInstance: i18n.i18n,
      ) => new I18NextService(errHandle, i18nextInstance),
      deps: [I18NEXT_ERROR_HANDLING_STRATEGY, I18NEXT_INSTANCE],
    },
    {
      provide: I18NEXT_NAMESPACE,
      useValue: '',
    },
    {
      provide: I18NEXT_SCOPE,
      useValue: '',
    },
    {
      provide: I18NEXT_ERROR_HANDLING_STRATEGY,
      useClass: NativeErrorHandlingStrategy,
    },
    {
      provide: LOCALE_ID,
      useFactory: localeIdFactory,
    },
    I18NextService,
    I18NextPipe,
    I18NextEagerPipe,
    I18NextCapPipe,
    I18NextFormatPipe,
  ];

  for (const feature of features) {
    providers.push(...feature.ɵproviders);
  }

  return makeEnvironmentProviders(providers);
}

/**
 * Configures a custom error handling strategy for i18next.
 *
 * @param errorHandlingStrategy - A class implementing the I18NextErrorHandlingStrategy interface.
 * @returns An I18NextFeature for the specified custom error handling strategy.
 *
 * This feature allows the integration of a custom error handling mechanism
 * into the i18next setup, replacing the default error handling strategy.
 *
 *  * Example:
 * ```typescript
 *    providers: [
 *       provideI18Next(withCustomErrorHandlingStrategy(StrictErrorHandlingStrategy)())
 *    ]
 * ```
 */
export function withCustomErrorHandlingStrategy(
  errorHandlingStrategy: Type<I18NextErrorHandlingStrategy>,
): I18NextFeature<I18NextFeatureKind.CustomErrorHandlingStrategy> {
  return makeI18NextFeature(I18NextFeatureKind.CustomErrorHandlingStrategy, [
    {
      provide: I18NEXT_ERROR_HANDLING_STRATEGY,
      useClass: errorHandlingStrategy,
    },
  ]);
}

/**
 * Provides I18NextTitle service for document title translation support.
 *
 * @returns An I18NextFeature that configures the I18NextTitle service
 *
 * Example:
 * ```typescript
 * providers: [
 *   provideI18Next(withTitle())
 * ]
 * ```
 */
export function withTitle(): I18NextFeature<I18NextFeatureKind.Title> {
  return makeI18NextFeature(I18NextFeatureKind.Title, [
    {
        provide: Title,
        useClass: I18NextTitle
    }
  ]);
}


================================================
FILE: libs/angular-i18next/src/lib/provider.utils.ts
================================================
import { Provider } from "@angular/core";

/**
 * A feature for use when configuring `provideI18Next`.
 *
 * @publicApi
 */
export interface I18NextFeature<KindT extends I18NextFeatureKind> {
  ɵkind: KindT;
  ɵproviders: Provider[];
}

export function makeI18NextFeature<KindT extends I18NextFeatureKind>(
  kind: KindT,
  providers: Provider[],
): I18NextFeature<KindT> {
  return {
    ɵkind: kind,
    ɵproviders: providers,
  };
}

/**
 * Identifies a particular kind of `HttpFeature`.
 *
 * @publicApi
 */
export enum I18NextFeatureKind {
  CustomErrorHandlingStrategy,
  Mock,
  Title,
  AppInitialize,
  SSR,
  Forms
}


================================================
FILE: libs/angular-i18next/src/lib/services/i18next-title.ts
================================================
import { DOCUMENT } from '@angular/common';
import { Inject, Injectable } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { I18NextPipe } from '../pipes/i18next.pipe';

@Injectable()
export class I18NextTitle extends Title {
  constructor(private i18nextPipe: I18NextPipe, @Inject(DOCUMENT) doc: any) {
    super(doc);
  }

  override setTitle(value: string) {
    return super.setTitle(this.translate(value));
  }

  private translate(text: string) {
    return this.i18nextPipe.transform(text, { format: 'cap' });
  }
}


================================================
FILE: libs/angular-i18next/src/lib/services/i18next.service.ts
================================================
import { Inject, Injectable, Optional } from '@angular/core';
import * as i18n from 'i18next';
import { I18NextErrorHandlingStrategy } from '../I18NextErrorHandlingStrategies';
import { I18NextEvents } from '../I18NextEvents';
import { I18NextLoadResult } from '../I18NextLoadResult';
import { I18NEXT_ERROR_HANDLING_STRATEGY, I18NEXT_INSTANCE } from '../tokens';
import { ITranslationEvents } from './translation.events';
import { ITranslationOptions, ITranslationService } from './translation.service';
import type { InitOptions, Module, Modules, Namespace, Newable, NewableModule, ResourceStore, Services, TFunction, TFunctionReturn } from 'i18next';


const i18nextGlobal: i18n.i18n = i18n.default;

@Injectable()
export class I18NextService implements ITranslationService {

  private readonly i18next: i18n.i18n;

  events: ITranslationEvents = new I18NextEvents();

  get language() {
    return this.i18next.language;
  }
  get languages() {
    return this.i18next.languages;
  }

  get options() {
    return this.i18next.options;
  }

  get modules(): Modules {
    return this.i18next.modules;
  }
  get services(): Services {
    return this.i18next.services;
  }
  get store(): ResourceStore {
    return this.i18next.store;
  }

  get resolvedLanguage() {
    return this.i18next.resolvedLanguage;
  }

  get isInitialized() {
    return this.i18next.isInitialized;
  }

  constructor(
    @Inject(I18NEXT_ERROR_HANDLING_STRATEGY)
    private errorHandlingStrategy: I18NextErrorHandlingStrategy,
    @Optional() @Inject(I18NEXT_INSTANCE) i18nextInstance?: i18n.i18n
  ) {
    this.i18next = i18nextInstance ?? i18nextGlobal;
  }

  t(key: string | string[], options?: ITranslationOptions | undefined): TFunctionReturn<Namespace, string | string[], ITranslationOptions>;
  t(key: string | string[] | (string | TemplateStringsArray)[], defaultValue: string, options?: ITranslationOptions | undefined): TFunctionReturn<Namespace, string | string[], ITranslationOptions>;
  t(key: unknown, defaultValueOrOptions?: unknown, options?: unknown): TFunctionReturn<Namespace, unknown, ITranslationOptions> {
    const hasDefault = !!defaultValueOrOptions && typeof defaultValueOrOptions === 'string';

    this.i18next.t.bind(this.i18next);
    if (hasDefault) {
      return this.i18next.t(key as (string | string[]), defaultValueOrOptions as string, options as ITranslationOptions);
    } else {
      return this.i18next.t(key as (string | string[]), defaultValueOrOptions as ITranslationOptions);
    }
  }

  public use<T extends Module>(
    module:
    T | NewableModule<T> | Newable<T>
  ): ITranslationService {
    this.i18next.use.call(this.i18next, module);
    return this;
  }

  init(options: InitOptions): Promise<I18NextLoadResult> {
    this.subscribeEvents();

    return new Promise<I18NextLoadResult>((resolve, reject) => {
      this.i18next.init.call(
        this.i18next,
        Object.assign({}, options ?? {}),
        this.errorHandlingStrategy.handle(resolve, reject)
      );
    });
  }

  public format(value: any, format?: string, lng?: string): string {
    return this.i18next.format.call(this.i18next, value, format, lng, {});
  }

  public exists(key: string | string[], options: any) {
    return this.i18next.exists.call(this.i18next, key, options);
  }

  getFixedT(lng: string | readonly string[], ns?: string | readonly string[], keyPrefix?: string): TFunction;
  getFixedT(lng: null, ns: string | readonly string[] | null, keyPrefix?: string): TFunction;
  getFixedT(lng: any, ns?: any, keyPrefix?: any): TFunction {
    return this.i18next.getFixedT.call(this.i18next, lng, ns, keyPrefix);
  }

  public setDefaultNamespace(ns: string) {
    this.i18next.setDefaultNamespace.call(this.i18next, ns);
  }

  public dir(lng?: string) {
    return this.i18next.dir.call(this.i18next, lng);
  }

  public changeLanguage(lng: string): Promise<TFunction> {
    return new Promise<TFunction>(
      (
        resolve: (thenableOrResult: TFunction | PromiseLike<TFunction>) => void,
        reject: (error: any) => void
      ) => {
        return this.i18next.changeLanguage.call(
          this.i18next,
          lng,
          this.errorHandlingStrategy.handle(resolve, reject)
        );
      }
    );
  }

  public loadNamespaces(namespaces: string | string[]): Promise<any> {
    return new Promise<I18NextLoadResult>(
      (
        resolve: (thenableOrResult: I18NextLoadResult | PromiseLike<I18NextLoadResult>) => void,
        reject: (error: any) => void
      ) => {
        this.i18next.loadNamespaces.call(
          this.i18next,
          namespaces,
          this.errorHandlingStrategy.handle(resolve, reject)
        );
      }
    );
  }

  public loadLanguages(lngs: string | string[]) {
    return new Promise<void>(
      (
        resolve: (thenableOrResult: void | PromiseLike<void>) => void,
        reject: (error: any) => void
      ) => {
        this.i18next.loadLanguages.call(
          this.i18next,
          lngs,
          this.errorHandlingStrategy.handle(resolve, reject)
        );
      }
    );
  }

  //#region resource handling

  public loadResources(callback?: (err: any) => void): void {
    this.i18next.loadResources.call(this.i18next, callback);
  }
  public getDataByLanguage(lng: string) {
    return this.i18next.getDataByLanguage.call(this.i18next, lng);
  }

  public async reloadResources(...params: any) {
    await this.i18next.reloadResources.apply(this.i18next, params);
  }

  public getResource(lng: string, ns: string, key: string, options: any) {
    return this.i18next.getResource.call(this.i18next, lng, ns, key, options);
  }

  public addResource(lng: string, ns: string, key: string, value: any, options: any): i18n.i18n {
    return this.i18next.addResource.call(this.i18next, lng, ns, key, value, options);
  }

  public addResources(lng: string, ns: string, resources: any): i18n.i18n {
    return this.i18next.addResources.call(this.i18next, lng, ns, resources);
  }

  public addResourceBundle(lng: string, ns: string, resources: any, deep: any, overwrite: any): i18n.i18n {
    return this.i18next.addResourceBundle.call(
      this.i18next,
      lng,
      ns,
      resources,
      deep,
      overwrite
    );
  }

  public hasResourceBundle(lng: string, ns: string) {
    return this.i18next.hasResourceBundle.call(this.i18next, lng, ns);
  }

  public getResourceBundle(lng: string, ns: string) {
    return this.i18next.getResourceBundle.call(this.i18next, lng, ns);
  }

  public removeResourceBundle(lng: string, ns: string): i18n.i18n {
    return this.i18next.removeResourceBundle.call(this.i18next, lng, ns);
  }

  //#endregion

  private subscribeEvents() {
    this.i18next.on.call(this.i18next, 'initialized', (options: InitOptions) => {
      this.events.initialized.next(options);
    });
    this.i18next.on.call(this.i18next, 'loaded', (loaded: boolean) =>
      this.events.loaded.next(loaded)
    );
    this.i18next.on.call(this.i18next, 'failedLoading', (lng: string, ns: string, msg: string) =>
      this.events.failedLoading.next({ lng, ns, msg })
    );
    this.i18next.on.call(this.i18next, 'languageChanged', (lng: string) => {
      this.events.languageChanged.next(lng);
    });
    this.i18next.on.call(this.i18next, 'missingKey', (lngs: string, namespace: string, key: string, res: any) =>
      this.events.missingKey.next({ lngs, namespace, key, res })
    );
    this.i18next.on.call(this.i18next, 'added', (lng: string, ns: string) =>
      this.events.added.next({ lng, ns })
    );
    this.i18next.on.call(this.i18next, 'removed', (lng: string, ns: string) =>
      this.events.removed.next({ lng, ns })
    );
  }
}


================================================
FILE: libs/angular-i18next/src/lib/services/translation.events.ts
================================================
import { BehaviorSubject, Subject } from 'rxjs';
import * as i18n from 'i18next';

export type ResourceEvent = { lng: any; ns: any };
export type MissingKeyEvent = { lngs: any; namespace: any; key: any; res: any };

export interface ITranslationEvents {
  initialized: BehaviorSubject<i18n.InitOptions | undefined>;
  loaded: BehaviorSubject<boolean>;
  failedLoading: Subject<any>;
  missingKey: Subject<MissingKeyEvent>;
  added: Subject<ResourceEvent>;
  removed: Subject<ResourceEvent>;
  languageChanged: BehaviorSubject<string | null>;
}


================================================
FILE: libs/angular-i18next/src/lib/services/translation.service.ts
================================================
import * as i18n from 'i18next';
import { I18NextLoadResult } from '../I18NextLoadResult';
import { ITranslationEvents } from './translation.events';
import type { Callback, ExistsFunction, FormatFunction, InitOptions, Module, Modules, Namespace, Newable, NewableModule, ResourceStore, Services, TFunction, TOptions } from 'i18next';

type Modify<T, R> = Omit<T, keyof R> & R;

export type ITranslationOptions = TOptions;

export type ITranslationService = Modify<Partial<i18n.i18n>, {

  events: ITranslationEvents;

  language: string;
  languages: readonly string[];
  options: InitOptions;
  modules: Modules;
  services: Services;
  store: ResourceStore;
  resolvedLanguage: string | undefined;

  use<T extends Module>(
    module:
    T | NewableModule<T> | Newable<T>
  ): ITranslationService;

  init(options: InitOptions): Promise<I18NextLoadResult>;

  t<Options extends ITranslationOptions>(
    key: string | string[],
    options?: Options,
  ): i18n.TFunctionReturn<Namespace, string | string[], Options>;
  t<Options extends ITranslationOptions>(
    key: string | string[],
    defaultValue: string,
    options?: Options
  ): i18n.TFunctionReturn<Namespace, string | string[], Options>;

  format: FormatFunction;

  exists: ExistsFunction;

  getFixedT(
    lng: string | readonly string[],
    ns?: string | readonly string[],
    keyPrefix?: string,
  ): TFunction;
  getFixedT(lng: null, ns: string | readonly string[] | null, keyPrefix?: string): TFunction;

  setDefaultNamespace(ns: string): void;

  dir(lng: string): string;

  changeLanguage(lng: string): Promise<any>;

  loadNamespaces(namespaces: string[]): Promise<any>;
  loadLanguages(lngs: string | readonly string[], callback?: Callback): Promise<void>;

  loadResources(callback?: (err: any) => void): void;

  getDataByLanguage(lng: string): {
    [key: string]: {
        [key: string]: string;
    };
} | undefined;

  reloadResources(
    lngs?: string | readonly string[],
    ns?: string | readonly string[],
    callback?: () => void,
  ): Promise<void>;
  reloadResources(lngs: null, ns: string | readonly string[], callback?: () => void): Promise<void>;

  getResource(
    lng: string,
    ns: string,
    key: string,
    options?: Pick<i18n.InitOptions, 'keySeparator' | 'ignoreJSONStructure'>,
  ): any;
  addResource(
    lng: string,
    ns: string,
    key: string,
    value: string,
    options?: { keySeparator?: string; silent?: boolean },
  ): i18n.i18n;
  addResources(lng: string, ns: string, resources: any): i18n.i18n;
  addResourceBundle(
    lng: string,
    ns: string,
    resources: any,
    deep?: boolean,
    overwrite?: boolean,
  ): i18n.i18n;
  hasResourceBundle(lng: string, ns: string): boolean;
  getResourceBundle(lng: string, ns: string): any;
  removeResourceBundle(lng: string, ns: string): i18n.i18n;

}>;


================================================
FILE: libs/angular-i18next/src/lib/tokens.ts
================================================
import * as i18n from 'i18next'
import { InjectionToken } from '@angular/core';
import { I18NextErrorHandlingStrategy } from './I18NextErrorHandlingStrategies';
import { ITranslationService } from './services/translation.service';
import { NamespaceResolver } from './models';

export const I18NEXT_SCOPE = new InjectionToken<string | string[]>(
  'I18NEXT_SCOPE'
);
export const I18NEXT_NAMESPACE = new InjectionToken<string | string[]>(
  'I18NEXT_NAMESPACE'
);
export const I18NEXT_SERVICE = new InjectionToken<ITranslationService>(
  'I18NEXT_SERVICE'
);
export const I18NEXT_NAMESPACE_RESOLVER = new InjectionToken<N
Download .txt
gitextract_w52vdt87/

├── .cleandir.sh
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       └── static.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode/
│   ├── extensions.json
│   ├── launch.json
│   └── settings.json
├── CHANGELOG.md
├── FUNDING.yml
├── LICENSE
├── README.md
├── README_DEPRECATED.md
├── apps/
│   └── angular-i18next-demo/
│       ├── .browserslistrc
│       ├── .eslintrc.json
│       ├── jest.config.ts
│       ├── project.json
│       ├── src/
│       │   ├── app/
│       │   │   ├── app.component.css
│       │   │   ├── app.component.html
│       │   │   ├── app.component.ts
│       │   │   ├── app.config.server.ts
│       │   │   ├── app.config.ts
│       │   │   ├── app.routes.server.ts
│       │   │   ├── app.routes.ts
│       │   │   ├── content/
│       │   │   │   ├── access-denied/
│       │   │   │   │   ├── access-denied.component.html
│       │   │   │   │   └── access-denied.component.ts
│       │   │   │   ├── simple-demo.component.html
│       │   │   │   └── simple-demo.component.ts
│       │   │   ├── features/
│       │   │   │   └── rich_form_feature/
│       │   │   │       ├── rich-form.component.html
│       │   │   │       ├── rich-form.component.ts
│       │   │   │       └── rich-form.model.ts
│       │   │   ├── i18next.options.ts
│       │   │   └── structure/
│       │   │       ├── app-error.component.html
│       │   │       ├── app-error.component.ts
│       │   │       ├── app-footer.component.html
│       │   │       ├── app-footer.component.ts
│       │   │       ├── app-header.component.html
│       │   │       ├── app-header.component.ts
│       │   │       └── header-controls/
│       │   │           ├── header.language.component.html
│       │   │           └── header.language.component.ts
│       │   ├── assets/
│       │   │   ├── .gitkeep
│       │   │   └── ng-validation.css
│       │   ├── environments/
│       │   │   ├── environment.prod.ts
│       │   │   └── environment.ts
│       │   ├── index.html
│       │   ├── lib/
│       │   │   └── validation/
│       │   │       ├── services/
│       │   │       │   └── ValidationDirtyChecker.ts
│       │   │       └── validators/
│       │   │           ├── ArrayValidators.js
│       │   │           ├── ArrayValidators.ts
│       │   │           ├── ConditionalValidator.js
│       │   │           └── ConditionalValidator.ts
│       │   ├── locales/
│       │   │   ├── en.error.json
│       │   │   ├── en.feature.rich_form.json
│       │   │   ├── en.translation.json
│       │   │   ├── en.validation.json
│       │   │   ├── ru.error.json
│       │   │   ├── ru.feature.rich_form.json
│       │   │   ├── ru.translation.json
│       │   │   └── ru.validation.json
│       │   ├── main.server.ts
│       │   ├── main.ts
│       │   ├── server.ts
│       │   ├── styles.css
│       │   └── test-setup.ts
│       ├── tsconfig.app.json
│       ├── tsconfig.editor.json
│       ├── tsconfig.json
│       ├── tsconfig.server.json
│       └── tsconfig.spec.json
├── decorate-angular-cli.js
├── ecosystem.config.js
├── jest.config.ts
├── jest.preset.js
├── libs/
│   └── angular-i18next/
│       ├── .eslintrc.json
│       ├── CHANGELOG.md
│       ├── README.md
│       ├── forms/
│       │   ├── ng-package.json
│       │   └── src/
│       │       ├── components/
│       │       │   └── validation-message.component.ts
│       │       ├── directives/
│       │       │   └── validation-message.directive.ts
│       │       ├── models.ts
│       │       └── public_api.ts
│       ├── jest.config.ts
│       ├── ng-package.json
│       ├── package.json
│       ├── postinstall.js
│       ├── project.json
│       ├── src/
│       │   ├── .eslintrc.json
│       │   ├── index.ts
│       │   ├── lib/
│       │   │   ├── I18NextErrorHandlingStrategies.ts
│       │   │   ├── I18NextEvents.ts
│       │   │   ├── I18NextLoadResult.ts
│       │   │   ├── I18NextModuleParams.ts
│       │   │   ├── index.ts
│       │   │   ├── interpolation.ts
│       │   │   ├── models.ts
│       │   │   ├── module.ts
│       │   │   ├── namespace.resolver.ts
│       │   │   ├── namespaces.guard.ts
│       │   │   ├── pipes/
│       │   │   │   ├── i18next-cap.pipe.ts
│       │   │   │   ├── i18next-eager.pipe.ts
│       │   │   │   ├── i18next-format.pipe.ts
│       │   │   │   └── i18next.pipe.ts
│       │   │   ├── provider.ts
│       │   │   ├── provider.utils.ts
│       │   │   ├── services/
│       │   │   │   ├── i18next-title.ts
│       │   │   │   ├── i18next.service.ts
│       │   │   │   ├── translation.events.ts
│       │   │   │   └── translation.service.ts
│       │   │   └── tokens.ts
│       │   ├── test-setup.ts
│       │   └── tests/
│       │       ├── module/
│       │       │   └── module.spec.ts
│       │       ├── pipes/
│       │       │   ├── I18NextEagerPipe.spec.ts
│       │       │   └── I18NextPipe.spec.ts
│       │       ├── projectTests/
│       │       │   ├── project.component.ts
│       │       │   └── projectTests.spec.ts
│       │       ├── provider/
│       │       │   └── provider.spec.ts
│       │       ├── service/
│       │       │   └── I18NextService.spec.ts
│       │       └── setup.ts
│       ├── ssr/
│       │   ├── ng-package.json
│       │   └── src/
│       │       ├── provider.ssr.ts
│       │       └── public_api.ts
│       ├── testing/
│       │   ├── ng-package.json
│       │   └── src/
│       │       ├── mock.service.ts
│       │       ├── provider.ts
│       │       └── public_api.ts
│       ├── tsconfig.json
│       ├── tsconfig.lib.json
│       └── tsconfig.spec.json
├── migrations.json
├── nx.json
├── package.json
├── tools/
│   └── tsconfig.tools.json
├── tsconfig.base.json
└── tsconfig.json
Download .txt
SYMBOL INDEX (181 symbols across 46 files)

FILE: apps/angular-i18next-demo/src/app/app.component.ts
  class AppComponent (line 21) | class AppComponent implements OnInit  {
    method title (line 26) | get title() {
    method constructor (line 30) | constructor(private router: Router,
    method ngOnInit (line 56) | ngOnInit() {
    method navigationInterceptor (line 74) | navigationInterceptor(event: RouterEvent): void {
    method updatePageTitle (line 85) | updatePageTitle(title: string): void {

FILE: apps/angular-i18next-demo/src/app/app.config.ts
  function appInit (line 23) | function appInit() {

FILE: apps/angular-i18next-demo/src/app/content/access-denied/access-denied.component.ts
  class AccessDeniedComponent (line 11) | class AccessDeniedComponent {

FILE: apps/angular-i18next-demo/src/app/content/simple-demo.component.ts
  class SimpleDemoComponent (line 13) | class SimpleDemoComponent {

FILE: apps/angular-i18next-demo/src/app/features/rich_form_feature/rich-form.component.ts
  class RichFormComponent (line 17) | class RichFormComponent {
    method constructor (line 22) | constructor(private fb: FormBuilder, private readonly validationDirtyC...
    method onSubmit (line 35) | onSubmit(e: Event) {

FILE: apps/angular-i18next-demo/src/app/features/rich_form_feature/rich-form.model.ts
  class Contact (line 1) | class Contact {
  class RichFormModel (line 7) | class RichFormModel {

FILE: apps/angular-i18next-demo/src/app/structure/app-error.component.ts
  class AppErrorComponent (line 12) | class AppErrorComponent {
    method toggle (line 14) | public toggle(){
    method close (line 17) | public close(){
    method reload (line 20) | public reload(){

FILE: apps/angular-i18next-demo/src/app/structure/app-footer.component.ts
  class AppFooterComponent (line 10) | class AppFooterComponent {
    method constructor (line 13) | constructor(){

FILE: apps/angular-i18next-demo/src/app/structure/app-header.component.ts
  class AppHeaderComponent (line 13) | class AppHeaderComponent {}

FILE: apps/angular-i18next-demo/src/app/structure/header-controls/header.language.component.ts
  class HeaderLanguageComponent (line 12) | class HeaderLanguageComponent implements OnInit {
    method constructor (line 17) | constructor(
    method ngOnInit (line 22) | ngOnInit() {
    method changeLanguage (line 30) | changeLanguage(lang: string){
    method updateState (line 39) | private updateState(lang: string) {

FILE: apps/angular-i18next-demo/src/lib/validation/services/ValidationDirtyChecker.ts
  class ValidationDirtyChecker (line 5) | class ValidationDirtyChecker {
    method markControlsDirty (line 6) | markControlsDirty(group: FormGroup | FormArray) {

FILE: apps/angular-i18next-demo/src/lib/validation/validators/ArrayValidators.js
  function ArrayValidators (line 2) | function ArrayValidators() {

FILE: apps/angular-i18next-demo/src/lib/validation/validators/ArrayValidators.ts
  class ArrayValidators (line 4) | class ArrayValidators {
    method minLength (line 5) | static minLength(minLength: Number, ignoreNullAndUndefined: Boolean = ...
    method maxLength (line 23) | static maxLength(maxLength: Number, ignoreNullAndUndefined: Boolean = ...
    method eqLength (line 42) | static eqLength(length: Number, ignoreNullAndUndefined: Boolean = fals...

FILE: apps/angular-i18next-demo/src/lib/validation/validators/ConditionalValidator.js
  function ConditionalValidator (line 3) | function ConditionalValidator() {
  function revalidateOnChanges (line 42) | function revalidateOnChanges(control, trackParentOnly) {

FILE: apps/angular-i18next-demo/src/lib/validation/validators/ConditionalValidator.ts
  type ConditionalFunc (line 44) | interface ConditionalFunc {
  class ConditionalValidator (line 49) | class ConditionalValidator {
    method set (line 56) | static set(conditional: ConditionalFunc, validator: ValidatorFn, track...
    method setAsync (line 73) | static setAsync(conditional: Function, validator: AsyncValidatorFn): A...
    method equivalent (line 77) | static equivalent(controlKey: string, expectedValue: any): Conditional...
  function revalidateOnChanges (line 87) | function revalidateOnChanges(control: AbstractControl, trackParentOnly: ...

FILE: apps/angular-i18next-demo/src/main.ts
  function bootstrap (line 11) | function bootstrap() {

FILE: decorate-angular-cli.js
  function symlinkNgCLItoNxCLI (line 42) | function symlinkNgCLItoNxCLI() {

FILE: libs/angular-i18next/forms/src/components/validation-message.component.ts
  class I18NextValidationMessageComponent (line 31) | class I18NextValidationMessageComponent {
    method setFor (line 40) | setFor(control: NgControl) {
    method constructor (line 44) | constructor() {
    method getErrorMessages (line 94) | private getErrorMessages(control: NgControl) {

FILE: libs/angular-i18next/forms/src/directives/validation-message.directive.ts
  class I18NextValidationMessageDirective (line 9) | class I18NextValidationMessageDirective implements AfterViewInit, OnDest...
    method ngAfterViewInit (line 15) | ngAfterViewInit(): void {
    method ngOnDestroy (line 24) | ngOnDestroy(): void {
    method detach (line 28) | private detach(): void {

FILE: libs/angular-i18next/forms/src/models.ts
  type ValidationMessageParams (line 4) | interface ValidationMessageParams {
  class ValidationMessage (line 8) | class ValidationMessage {
    method constructor (line 9) | constructor(

FILE: libs/angular-i18next/src/lib/I18NextErrorHandlingStrategies.ts
  type I18NextErrorHandlingStrategy (line 4) | interface I18NextErrorHandlingStrategy {
  class NativeErrorHandlingStrategy (line 11) | class NativeErrorHandlingStrategy
    method handle (line 14) | handle(
  class StrictErrorHandlingStrategy (line 28) | class StrictErrorHandlingStrategy
    method handle (line 31) | handle(

FILE: libs/angular-i18next/src/lib/I18NextEvents.ts
  class I18NextEvents (line 10) | class I18NextEvents implements ITranslationEvents {

FILE: libs/angular-i18next/src/lib/I18NextLoadResult.ts
  type I18NextLoadResult (line 1) | interface I18NextLoadResult {

FILE: libs/angular-i18next/src/lib/I18NextModuleParams.ts
  type I18NextModuleParams (line 4) | interface I18NextModuleParams {

FILE: libs/angular-i18next/src/lib/interpolation.ts
  function defaultInterpolationFormat (line 3) | function defaultInterpolationFormat(
  function interpolationFormat (line 27) | function interpolationFormat(customFormat: Function | null = null): Form...

FILE: libs/angular-i18next/src/lib/models.ts
  type FormatPipeOptions (line 3) | type FormatPipeOptions = { format?: string; lng?: string; case?: string;...
  type PrependPipeOptions (line 4) | type PrependPipeOptions = {
  type PipeOptions (line 10) | type PipeOptions = i18n.TOptions &
  type NamespaceResolver (line 14) | type NamespaceResolver = (

FILE: libs/angular-i18next/src/lib/module.ts
  class I18NextModule (line 45) | class I18NextModule {
    method forRoot (line 57) | static forRoot(
    method interpolationFormat (line 93) | static interpolationFormat(customFormat: Function | null = null): Form...

FILE: libs/angular-i18next/src/lib/namespace.resolver.ts
  function resolver (line 6) | function resolver(
  function i18nextNamespaceResolverFactory (line 16) | function i18nextNamespaceResolverFactory() {

FILE: libs/angular-i18next/src/lib/pipes/i18next-cap.pipe.ts
  class I18NextCapPipe (line 16) | class I18NextCapPipe extends I18NextPipe implements PipeTransform {
    method constructor (line 17) | constructor(
    method transform (line 25) | public override transform(key: string | string[], options?: PipeOption...

FILE: libs/angular-i18next/src/lib/pipes/i18next-eager.pipe.ts
  class I18NextEagerPipe (line 22) | class I18NextEagerPipe
    method constructor (line 30) | constructor(
    method hasKeyChanged (line 43) | private hasKeyChanged(key: string | string[]): boolean {
    method hasOptionsChanged (line 47) | private hasOptionsChanged(options?: PipeOptions): boolean {
    method transform (line 51) | public override transform(key: string | string[], options?: PipeOption...

FILE: libs/angular-i18next/src/lib/pipes/i18next-format.pipe.ts
  class I18NextFormatPipe (line 11) | class I18NextFormatPipe implements PipeTransform {
    method constructor (line 12) | constructor(
    method transform (line 16) | public transform(value: any, options: FormatPipeOptions | string): str...

FILE: libs/angular-i18next/src/lib/pipes/i18next.pipe.ts
  class I18NextPipe (line 15) | class I18NextPipe implements PipeTransform {
    method constructor (line 16) | constructor(
    method transform (line 22) | public transform(key: string | string[], options?: PipeOptions): string {
    method prependScope (line 59) | private prependScope(
    method prependNamespace (line 87) | private prependNamespace(
    method joinStrings (line 111) | private joinStrings(separator: string, ...str: string[]) {
    method keyContainsNsSeparator (line 115) | private keyContainsNsSeparator(key: string, nsSeparator: string) {
    method prepareOptions (line 119) | private prepareOptions(options?: PipeOptions): PipeOptions {

FILE: libs/angular-i18next/src/lib/provider.ts
  function localeIdFactory (line 33) | function localeIdFactory() {
  function provideI18Next (line 52) | function provideI18Next(
  function withCustomErrorHandlingStrategy (line 114) | function withCustomErrorHandlingStrategy(
  function withTitle (line 137) | function withTitle(): I18NextFeature<I18NextFeatureKind.Title> {

FILE: libs/angular-i18next/src/lib/provider.utils.ts
  type I18NextFeature (line 8) | interface I18NextFeature<KindT extends I18NextFeatureKind> {
  function makeI18NextFeature (line 13) | function makeI18NextFeature<KindT extends I18NextFeatureKind>(
  type I18NextFeatureKind (line 28) | enum I18NextFeatureKind {

FILE: libs/angular-i18next/src/lib/services/i18next-title.ts
  class I18NextTitle (line 7) | class I18NextTitle extends Title {
    method constructor (line 8) | constructor(private i18nextPipe: I18NextPipe, @Inject(DOCUMENT) doc: a...
    method setTitle (line 12) | override setTitle(value: string) {
    method translate (line 16) | private translate(text: string) {

FILE: libs/angular-i18next/src/lib/services/i18next.service.ts
  class I18NextService (line 15) | class I18NextService implements ITranslationService {
    method language (line 21) | get language() {
    method languages (line 24) | get languages() {
    method options (line 28) | get options() {
    method modules (line 32) | get modules(): Modules {
    method services (line 35) | get services(): Services {
    method store (line 38) | get store(): ResourceStore {
    method resolvedLanguage (line 42) | get resolvedLanguage() {
    method isInitialized (line 46) | get isInitialized() {
    method constructor (line 50) | constructor(
    method t (line 60) | t(key: unknown, defaultValueOrOptions?: unknown, options?: unknown): T...
    method use (line 71) | public use<T extends Module>(
    method init (line 79) | init(options: InitOptions): Promise<I18NextLoadResult> {
    method format (line 91) | public format(value: any, format?: string, lng?: string): string {
    method exists (line 95) | public exists(key: string | string[], options: any) {
    method getFixedT (line 101) | getFixedT(lng: any, ns?: any, keyPrefix?: any): TFunction {
    method setDefaultNamespace (line 105) | public setDefaultNamespace(ns: string) {
    method dir (line 109) | public dir(lng?: string) {
    method changeLanguage (line 113) | public changeLanguage(lng: string): Promise<TFunction> {
    method loadNamespaces (line 128) | public loadNamespaces(namespaces: string | string[]): Promise<any> {
    method loadLanguages (line 143) | public loadLanguages(lngs: string | string[]) {
    method loadResources (line 160) | public loadResources(callback?: (err: any) => void): void {
    method getDataByLanguage (line 163) | public getDataByLanguage(lng: string) {
    method reloadResources (line 167) | public async reloadResources(...params: any) {
    method getResource (line 171) | public getResource(lng: string, ns: string, key: string, options: any) {
    method addResource (line 175) | public addResource(lng: string, ns: string, key: string, value: any, o...
    method addResources (line 179) | public addResources(lng: string, ns: string, resources: any): i18n.i18n {
    method addResourceBundle (line 183) | public addResourceBundle(lng: string, ns: string, resources: any, deep...
    method hasResourceBundle (line 194) | public hasResourceBundle(lng: string, ns: string) {
    method getResourceBundle (line 198) | public getResourceBundle(lng: string, ns: string) {
    method removeResourceBundle (line 202) | public removeResourceBundle(lng: string, ns: string): i18n.i18n {
    method subscribeEvents (line 208) | private subscribeEvents() {

FILE: libs/angular-i18next/src/lib/services/translation.events.ts
  type ResourceEvent (line 4) | type ResourceEvent = { lng: any; ns: any };
  type MissingKeyEvent (line 5) | type MissingKeyEvent = { lngs: any; namespace: any; key: any; res: any };
  type ITranslationEvents (line 7) | interface ITranslationEvents {

FILE: libs/angular-i18next/src/lib/services/translation.service.ts
  type Modify (line 6) | type Modify<T, R> = Omit<T, keyof R> & R;
  type ITranslationOptions (line 8) | type ITranslationOptions = TOptions;
  type ITranslationService (line 10) | type ITranslationService = Modify<Partial<i18n.i18n>, {

FILE: libs/angular-i18next/src/lib/tokens.ts
  constant I18NEXT_SCOPE (line 7) | const I18NEXT_SCOPE = new InjectionToken<string | string[]>(
  constant I18NEXT_NAMESPACE (line 10) | const I18NEXT_NAMESPACE = new InjectionToken<string | string[]>(
  constant I18NEXT_SERVICE (line 13) | const I18NEXT_SERVICE = new InjectionToken<ITranslationService>(
  constant I18NEXT_NAMESPACE_RESOLVER (line 16) | const I18NEXT_NAMESPACE_RESOLVER = new InjectionToken<NamespaceResolver>(
  constant I18NEXT_ERROR_HANDLING_STRATEGY (line 19) | const I18NEXT_ERROR_HANDLING_STRATEGY =
  constant I18NEXT_INSTANCE (line 24) | const I18NEXT_INSTANCE = new InjectionToken<i18n.i18n>('I18NEXT_INSTANCE');

FILE: libs/angular-i18next/src/tests/pipes/I18NextPipe.spec.ts
  function buildKeyWithNs (line 176) | function buildKeyWithNs(service: ITranslationService, ns: string, key: s...

FILE: libs/angular-i18next/src/tests/projectTests/project.component.ts
  class ProjectComponent (line 11) | class ProjectComponent {}

FILE: libs/angular-i18next/src/tests/provider/provider.spec.ts
  class CustomErrorStrategy (line 107) | class CustomErrorStrategy extends NativeErrorHandlingStrategy {}

FILE: libs/angular-i18next/src/tests/setup.ts
  constant MOCK_I18N_PROVIDERS (line 7) | const MOCK_I18N_PROVIDERS = [

FILE: libs/angular-i18next/ssr/src/provider.ssr.ts
  function withSSR (line 14) | function withSSR(): I18NextFeature<I18NextFeatureKind.SSR> {

FILE: libs/angular-i18next/testing/src/mock.service.ts
  class MockI18NextService (line 9) | class MockI18NextService implements ITranslationService {
    method isInitialized (line 13) | get isInitialized() {
    method modules (line 17) | get modules(): Modules {
    method services (line 20) | get services(): Services {
    method store (line 23) | get store(): ResourceStore {
    method resolvedLanguage (line 27) | get resolvedLanguage() {
    method constructor (line 31) | constructor(
    method getFixedT (line 54) | getFixedT(lng: any, ns?: any, keyPrefix?: any): import("i18next").TFun...
    method loadLanguages (line 57) | loadLanguages(lngs: string | readonly string[], callback?: Callback): ...
    method loadResources (line 60) | loadResources(callback?: (err: any) => void): void {
    method getDataByLanguage (line 63) | getDataByLanguage(lng: string): { translation: { [key: string]: string...
    method reloadResources (line 68) | reloadResources(lngs?: any, ns?: any, callback?: any): Promise<void> {
    method addResource (line 71) | addResource(lng: string, ns: string, key: string, value: string, optio...
    method addResources (line 74) | addResources(lng: string, ns: string, resources: any): i18n {
    method addResourceBundle (line 77) | addResourceBundle(lng: string, ns: string, resources: any, deep?: bool...
    method removeResourceBundle (line 80) | removeResourceBundle(lng: string, ns: string): i18n {
    method options (line 87) | get options(): any {
    method use (line 94) | public use(plugin: any): ITranslationService {
    method init (line 98) | public init(options?: any): Promise<I18NextLoadResult> {
    method changeLanguage (line 111) | public changeLanguage(lng: string): Promise<any> {
    method loadNamespaces (line 123) | public loadNamespaces(namespaces: string[]): Promise<any> {
    method exists (line 134) | exists(key: any, options: any) {
    method setDefaultNamespace (line 138) | setDefaultNamespace(ns: string) {}
    method dir (line 140) | dir(lng: string) {
    method getResource (line 144) | getResource(lng: any, ns: any, key: any, options: any) {
    method hasResourceBundle (line 148) | hasResourceBundle(lng: any, ns: any) {
    method getResourceBundle (line 152) | getResourceBundle(lng: any, ns: any) {

FILE: libs/angular-i18next/testing/src/provider.ts
  function mockAppInit (line 8) | function mockAppInit() {
  function withMock (line 35) | function withMock(): I18NextFeature<I18NextFeatureKind.Mock> {
Condensed preview — 137 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (247K chars).
[
  {
    "path": ".cleandir.sh",
    "chars": 142,
    "preview": "echo Cleaning up project directory...\nrm -rf node_modules dist build coverage src/**/*.d.ts src/**/*.js src/**/*.js.map "
  },
  {
    "path": ".editorconfig",
    "chars": 245,
    "preview": "# Editor configuration, see http://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = "
  },
  {
    "path": ".eslintignore",
    "chars": 13,
    "preview": "node_modules\n"
  },
  {
    "path": ".eslintrc.json",
    "chars": 733,
    "preview": "{\n  \"root\": true,\n  \"ignorePatterns\": [\"**/*\"],\n  \"plugins\": [\"@nx\"],\n  \"overrides\": [\n    {\n      \"files\": [\"*.ts\", \"*."
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 67,
    "preview": "custom: [\"https://www.paypal.com/paypalme2/sergeyromanchuk/10USD\"]\n"
  },
  {
    "path": ".github/workflows/static.yml",
    "chars": 1391,
    "preview": "# Simple workflow for deploying static content to GitHub Pages\nname: Deploy static content to Pages\n\non:\n  # Runs on pus"
  },
  {
    "path": ".gitignore",
    "chars": 516,
    "preview": "# See http://help.github.com/ignore-files/ for more about ignoring files.\n\n# compiled output\ndist\n/tmp\n/out-tsc\n\n# depen"
  },
  {
    "path": ".prettierignore",
    "chars": 95,
    "preview": "# Add files here to ignore them from prettier formatting\n\n/dist\n/coverage\n\n/.nx/cache\n.angular\n"
  },
  {
    "path": ".prettierrc",
    "chars": 26,
    "preview": "{\n  \"singleQuote\": true\n}\n"
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 217,
    "preview": "{\n  \"recommendations\": [\n    \"angular.ng-template\",\n    \"nrwl.angular-console\",\n    \"esbenp.prettier-vscode\",\n    \"first"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 459,
    "preview": "{\n  \"version\": \"1.0.0\",\n  \"configurations\": [\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"Jest"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 218,
    "preview": "// Place your settings in this file to overwrite default and user settings.\n{\n  \"search.exclude\": {\n    \"coverage\": true"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 9909,
    "preview": "# [10.3.0](https://github.com/Romanchuk/angular-i18next/compare/v10.3.0-0...v10.3.0) (2021-06-15)\n\n\n\n# [10.3.0-0](https:"
  },
  {
    "path": "FUNDING.yml",
    "chars": 18,
    "preview": "github: Romanchuk\n"
  },
  {
    "path": "LICENSE",
    "chars": 1073,
    "preview": "MIT License\n\nCopyright (c) 2017 Sergey Romanchuk\n\nPermission is hereby granted, free of charge, to any person obtaining "
  },
  {
    "path": "README.md",
    "chars": 12295,
    "preview": "[![npm version](https://badge.fury.io/js/angular-i18next.svg)](https://badge.fury.io/js/angular-i18next)\n[![Downloads](h"
  },
  {
    "path": "README_DEPRECATED.md",
    "chars": 14277,
    "preview": "[![npm version](https://badge.fury.io/js/angular-i18next.svg)](https://badge.fury.io/js/angular-i18next)\n[![Downloads](h"
  },
  {
    "path": "apps/angular-i18next-demo/.browserslistrc",
    "chars": 600,
    "preview": "# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.\n# For addit"
  },
  {
    "path": "apps/angular-i18next-demo/.eslintrc.json",
    "chars": 383,
    "preview": "{\n  \"extends\": [\"../../.eslintrc.json\"],\n  \"ignorePatterns\": [\"!**/*\"],\n  \"overrides\": [\n    {\n      \"files\": [\"*.ts\"],\n"
  },
  {
    "path": "apps/angular-i18next-demo/jest.config.ts",
    "chars": 724,
    "preview": "/* eslint-disable */\nexport default {\n  displayName: 'angular-i18next-demo',\n  preset: '../../jest.preset.js',\n  setupFi"
  },
  {
    "path": "apps/angular-i18next-demo/project.json",
    "chars": 2959,
    "preview": "{\n  \"name\": \"angular-i18next-demo\",\n  \"$schema\": \"../../node_modules/nx/schemas/project-schema.json\",\n  \"projectType\": \""
  },
  {
    "path": "apps/angular-i18next-demo/src/app/app.component.css",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "apps/angular-i18next-demo/src/app/app.component.html",
    "chars": 548,
    "preview": "<div class=\"app-wrapper\">\n  @if (loading) {\n  <div class=\"app-loader\">\n      <div class=\"loader-pusher\">\n          <div "
  },
  {
    "path": "apps/angular-i18next-demo/src/app/app.component.ts",
    "chars": 2974,
    "preview": "\nimport { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';\nimport { Title } from '@angular/platform-"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/app.config.server.ts",
    "chars": 580,
    "preview": "import { ApplicationConfig, mergeApplicationConfig } from '@angular/core';\nimport { provideServerRendering, withRoutes }"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/app.config.ts",
    "chars": 1398,
    "preview": "import { isPlatformBrowser } from '@angular/common';\nimport {\n  ApplicationConfig,\n  importProvidersFrom,\n  inject,\n  PL"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/app.routes.server.ts",
    "chars": 165,
    "preview": "import { RenderMode, ServerRoute } from '@angular/ssr';\n\nexport const serverRoutes: ServerRoute[] = [\n  {\n    path: '**'"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/app.routes.ts",
    "chars": 862,
    "preview": "import { Routes } from \"@angular/router\";\nimport { I18NEXT_NAMESPACE, i18NextNamespacesGuard } from \"angular-i18next\";\ni"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/content/access-denied/access-denied.component.html",
    "chars": 101,
    "preview": "<div class=\"main-header\">\n    <h2>{{ 'error:access_denied' | i18next: { case: 'cap' } }}</h2>\n</div>\n"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/content/access-denied/access-denied.component.ts",
    "chars": 271,
    "preview": "\nimport { Component } from '@angular/core';\nimport { I18NextPipe } from \"angular-i18next\";\n\n@Component({\n  selector: 'ac"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/content/simple-demo.component.html",
    "chars": 1767,
    "preview": "<div class=\"container\">\n    <br/>\n    <h1>{{ 'simple_demo' | i18nextCap }}</h1>\n    <br/>\n    <hr/>\n    <div class=\"row"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/content/simple-demo.component.ts",
    "chars": 485,
    "preview": "\nimport { Component, ViewEncapsulation } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { I"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/features/rich_form_feature/rich-form.component.html",
    "chars": 2796,
    "preview": "<div>\n    <div class=\"main-header\">\n        <h1>{{ 'title' | i18next: { case: 'cap' } }}</h1>\n    </div>\n    <br/>\n    "
  },
  {
    "path": "apps/angular-i18next-demo/src/app/features/rich_form_feature/rich-form.component.ts",
    "chars": 1595,
    "preview": "\nimport { Component, ViewEncapsulation } from '@angular/core';\nimport { FormBuilder, FormGroup, ReactiveFormsModule, Va"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/features/rich_form_feature/rich-form.model.ts",
    "chars": 255,
    "preview": "export class Contact {\n    lastName: string | undefined;\n    firstName: string | undefined;\n    middleName: string | und"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/i18next.options.ts",
    "chars": 1034,
    "preview": "import { defaultInterpolationFormat, interpolationFormat } from \"angular-i18next\";\nimport type * as i18n from 'i18next';"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/structure/app-error.component.html",
    "chars": 2414,
    "preview": "<div class=\"error-page\">\n    <div class=\"viewport\">\n        <div class=\"page-404\">\n            <div class=\"cols\">\n      "
  },
  {
    "path": "apps/angular-i18next-demo/src/app/structure/app-error.component.ts",
    "chars": 557,
    "preview": "\nimport { Component, ViewEncapsulation } from '@angular/core';\nimport { I18NextPipe } from 'angular-i18next';\n\n@Componen"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/structure/app-footer.component.html",
    "chars": 499,
    "preview": "<footer>\n    <nav class=\"fixed-bottom navbar navbar-expand-lg navbar-light\" style=\"background-color: #f2f2f2;\">\n       "
  },
  {
    "path": "apps/angular-i18next-demo/src/app/structure/app-footer.component.ts",
    "chars": 412,
    "preview": "\nimport { Component, VERSION, ViewEncapsulation } from '@angular/core';\n\n@Component({\n  selector: 'app-footer',\n  encap"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/structure/app-header.component.html",
    "chars": 855,
    "preview": "<header>\n    <nav class=\"navbar navbar-expand-lg navbar-dark\" style=\"background-color: #f2f2f2;\">\n        <div class=\"c"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/structure/app-header.component.ts",
    "chars": 523,
    "preview": "import { Component, ViewEncapsulation } from '@angular/core';\nimport { I18NextCapPipe, I18NextPipe } from 'angular-i18n"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/structure/header-controls/header.language.component.html",
    "chars": 354,
    "preview": "<div>\n  @for (lang of languages; track lang) {\n    @if (currentLanguage() !== lang) {\n      <a  href=\"javascript:void(0)"
  },
  {
    "path": "apps/angular-i18next-demo/src/app/structure/header-controls/header.language.component.ts",
    "chars": 1072,
    "preview": "\nimport { Component, Inject, ViewEncapsulation, OnInit, signal } from '@angular/core';\nimport { I18NEXT_SERVICE, I18Next"
  },
  {
    "path": "apps/angular-i18next-demo/src/assets/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "apps/angular-i18next-demo/src/assets/ng-validation.css",
    "chars": 954,
    "preview": "\ninput.ng-dirty.ng-invalid,\ntextarea.ng-dirty.ng-invalid,\n.form-control.ng-dirty.ng-invalid,\n.ng-dirty.ng-invalid:focus\n"
  },
  {
    "path": "apps/angular-i18next-demo/src/environments/environment.prod.ts",
    "chars": 51,
    "preview": "export const environment = {\n  production: true\n};\n"
  },
  {
    "path": "apps/angular-i18next-demo/src/environments/environment.ts",
    "chars": 267,
    "preview": "// This file can be replaced during build by using the `fileReplacements` array.\n// `ng build` replaces `environment.ts`"
  },
  {
    "path": "apps/angular-i18next-demo/src/index.html",
    "chars": 4302,
    "preview": "<!DOCTYPE html>\n<html>\n\n  <head>\n      <meta charset=\"utf-8\">\n      <meta http-equiv=\"x-ua-compatible\" content=\"ie=edge\""
  },
  {
    "path": "apps/angular-i18next-demo/src/lib/validation/services/ValidationDirtyChecker.ts",
    "chars": 572,
    "preview": "import { Injectable } from '@angular/core';\nimport { FormArray, FormGroup } from '@angular/forms';\n\n@Injectable()\nexport"
  },
  {
    "path": "apps/angular-i18next-demo/src/lib/validation/validators/ArrayValidators.js",
    "chars": 2669,
    "preview": "var ArrayValidators = (function () {\n    function ArrayValidators() {\n    }\n    ArrayValidators.minLength = function (mi"
  },
  {
    "path": "apps/angular-i18next-demo/src/lib/validation/validators/ArrayValidators.ts",
    "chars": 2196,
    "preview": "import { OnChanges, SimpleChanges, Component, Directive, forwardRef } from '@angular/core';\nimport { Validator, AsyncVal"
  },
  {
    "path": "apps/angular-i18next-demo/src/lib/validation/validators/ConditionalValidator.js",
    "chars": 2586,
    "preview": "import 'rxjs/add/operator/distinctUntilChanged'; // fn distinctUntilChanged\nvar ConditionalValidator = (function () {\n  "
  },
  {
    "path": "apps/angular-i18next-demo/src/lib/validation/validators/ConditionalValidator.ts",
    "chars": 3442,
    "preview": "import { Validator, AsyncValidatorFn, ValidatorFn, FormControl, FormGroup, FormArray, AbstractControl } from '@angular/f"
  },
  {
    "path": "apps/angular-i18next-demo/src/locales/en.error.json",
    "chars": 1423,
    "preview": "{\n  \"oops\": \"Oops!\",\n  \"error_occured\": \"Error has occured\",\n  \"error_occured_onload\": \"$t(error:error_occured)\",\n  \"acc"
  },
  {
    "path": "apps/angular-i18next-demo/src/locales/en.feature.rich_form.json",
    "chars": 243,
    "preview": "{\n    \"title\": \"Rich form with validation\",\n    \"technical_contact\": \"technical contact\",\n    \"count\": \"count\",\n    \"per"
  },
  {
    "path": "apps/angular-i18next-demo/src/locales/en.translation.json",
    "chars": 688,
    "preview": "{\n    \"application_title\": \"Demo: angular-i18next\",\n    \"intro\": \"This application is demonstrating itegration of i18ne"
  },
  {
    "path": "apps/angular-i18next-demo/src/locales/en.validation.json",
    "chars": 829,
    "preview": "{\n    \"required\": \"Field is required.\",\n    \"error\": \"Error occured.\",\n    \"min\": \"Minimum value is {{min}}. Was {{actu"
  },
  {
    "path": "apps/angular-i18next-demo/src/locales/ru.error.json",
    "chars": 1480,
    "preview": "{\n  \"oops\": \"Упс!\",\n  \"error_occured\": \"произошла ошибка\",\n  \"error_occured_onload\": \"При загрузке приложения $t(error:e"
  },
  {
    "path": "apps/angular-i18next-demo/src/locales/ru.feature.rich_form.json",
    "chars": 230,
    "preview": "{\n    \"title\": \"Форма с валидацией\",\n    \"technical_contact\": \"технический специалист\",\n    \"count\": \"кол-во\",\n    \"pers"
  },
  {
    "path": "apps/angular-i18next-demo/src/locales/ru.translation.json",
    "chars": 688,
    "preview": "{\n    \"application_title\": \"Демо: angular-i18next\",\n    \"intro\": \"Данное приложение демонстрирует интеграцию библиотеки"
  },
  {
    "path": "apps/angular-i18next-demo/src/locales/ru.validation.json",
    "chars": 1132,
    "preview": "{\n    \"required\": \"Заполните это поле.\",\n    \"error\": \"Возникла ошибка.\",\n    \"minValue\": \"Минимальное значение.\",\n    "
  },
  {
    "path": "apps/angular-i18next-demo/src/main.server.ts",
    "chars": 316,
    "preview": "import { bootstrapApplication, BootstrapContext } from '@angular/platform-browser';\nimport { AppComponent } from './app/"
  },
  {
    "path": "apps/angular-i18next-demo/src/main.ts",
    "chars": 562,
    "preview": "import { enableProdMode } from '@angular/core';\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport"
  },
  {
    "path": "apps/angular-i18next-demo/src/server.ts",
    "chars": 3257,
    "preview": "import {\n  AngularNodeAppEngine,\n  createNodeRequestHandler,\n  isMainModule,\n  writeResponseToNodeResponse,\n} from '@ang"
  },
  {
    "path": "apps/angular-i18next-demo/src/styles.css",
    "chars": 1033,
    "preview": "/* You can add global styles to this file, and also import other style files */\n\ninput.ng-dirty.ng-invalid,\ntextarea.ng-"
  },
  {
    "path": "apps/angular-i18next-demo/src/test-setup.ts",
    "chars": 104,
    "preview": "import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless';\n\nsetupZonelessTestEnv();\n"
  },
  {
    "path": "apps/angular-i18next-demo/tsconfig.app.json",
    "chars": 283,
    "preview": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"../../dist/out-tsc\",\n    \"types\": [\"node\"]\n  },\n"
  },
  {
    "path": "apps/angular-i18next-demo/tsconfig.editor.json",
    "chars": 119,
    "preview": "{\n  \"extends\": \"./tsconfig.json\",\n  \"include\": [\"**/*.ts\"],\n  \"compilerOptions\": {\n    \"types\": [\"jest\", \"node\"]\n  }\n}\n"
  },
  {
    "path": "apps/angular-i18next-demo/tsconfig.json",
    "chars": 723,
    "preview": "{\n  \"extends\": \"../../tsconfig.base.json\",\n  \"files\": [],\n  \"include\": [],\n  \"references\": [\n    {\n      \"path\": \"./tsco"
  },
  {
    "path": "apps/angular-i18next-demo/tsconfig.server.json",
    "chars": 443,
    "preview": "/* To learn more about this file see: https://angular.io/config/tsconfig. */\n{\n  \"extends\": \"./tsconfig.app.json\",\n  \"co"
  },
  {
    "path": "apps/angular-i18next-demo/tsconfig.spec.json",
    "chars": 371,
    "preview": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"../../dist/out-tsc\",\n    \"module\": \"commonjs\",\n "
  },
  {
    "path": "decorate-angular-cli.js",
    "chars": 2685,
    "preview": "/**\n * This file decorates the Angular CLI with the Nx CLI to enable features such as computation caching\n * and faster "
  },
  {
    "path": "ecosystem.config.js",
    "chars": 390,
    "preview": "const { cwd } = require(\"process\");\n\nmodule.exports = {\n  apps: [\n    {\n      name: 'angular-i18next-demo',\n      script"
  },
  {
    "path": "jest.config.ts",
    "chars": 63,
    "preview": "\nexport default {\n  projects: ['./libs/**/jest.config.ts'],\n};\n"
  },
  {
    "path": "jest.preset.js",
    "chars": 644,
    "preview": "const nxPreset = require('@nx/jest/preset').default;\n\nmodule.exports = {\n  ...nxPreset,\n  /* TODO: Update to latest Jest"
  },
  {
    "path": "libs/angular-i18next/.eslintrc.json",
    "chars": 73,
    "preview": "{\n  \"extends\": [\"../../.eslintrc.json\"],\n  \"ignorePatterns\": [\"!**/*\"]\n}\n"
  },
  {
    "path": "libs/angular-i18next/CHANGELOG.md",
    "chars": 23515,
    "preview": "# [21.0.0-3](https://github.com/Romanchuk/angular-i18next/compare/v20.0.1...v21.0.0-3) (2026-01-23)\n\n\n### Bug Fixes\n\n* b"
  },
  {
    "path": "libs/angular-i18next/README.md",
    "chars": 14406,
    "preview": "[![npm version](https://badge.fury.io/js/angular-i18next.svg)](https://badge.fury.io/js/angular-i18next)\n[![Downloads](h"
  },
  {
    "path": "libs/angular-i18next/forms/ng-package.json",
    "chars": 3,
    "preview": "{}\n"
  },
  {
    "path": "libs/angular-i18next/forms/src/components/validation-message.component.ts",
    "chars": 3428,
    "preview": "import { Component, ViewEncapsulation, computed, effect, inject, input, signal } from \"@angular/core\";\nimport { Abstract"
  },
  {
    "path": "libs/angular-i18next/forms/src/directives/validation-message.directive.ts",
    "chars": 1386,
    "preview": "import { AfterViewInit, ComponentRef, Directive, inject, OnDestroy, ViewContainerRef } from \"@angular/core\";\nimport { Fo"
  },
  {
    "path": "libs/angular-i18next/forms/src/models.ts",
    "chars": 291,
    "preview": "/**\n * Represents a validation message with translation key and parameters\n */\nexport interface ValidationMessageParams "
  },
  {
    "path": "libs/angular-i18next/forms/src/public_api.ts",
    "chars": 261,
    "preview": "export { ValidationMessage, ValidationMessageParams } from './models';\nexport { I18NextValidationMessageComponent } from"
  },
  {
    "path": "libs/angular-i18next/jest.config.ts",
    "chars": 730,
    "preview": "/* eslint-disable */\nexport default {\n  displayName: 'angular-i18next',\n  preset: '../../jest.preset.js',\n  setupFilesAf"
  },
  {
    "path": "libs/angular-i18next/ng-package.json",
    "chars": 180,
    "preview": "{\n  \"$schema\": \"../../node_modules/ng-packagr/ng-package.schema.json\",\n  \"assets\": [\n    \"CHANGELOG.md\",\n    \"postinstal"
  },
  {
    "path": "libs/angular-i18next/package.json",
    "chars": 1871,
    "preview": "{\n  \"name\": \"angular-i18next\",\n  \"version\": \"21.0.0-3\",\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"np\": {\n    \""
  },
  {
    "path": "libs/angular-i18next/postinstall.js",
    "chars": 412,
    "preview": "var BANNER = '\\u001B[96mThank you for using angular-i18next (https://github.com/Romanchuk/angular-i18next). Please star "
  },
  {
    "path": "libs/angular-i18next/project.json",
    "chars": 905,
    "preview": "{\n  \"name\": \"angular-i18next\",\n  \"$schema\": \"../../node_modules/nx/schemas/project-schema.json\",\n  \"projectType\": \"libra"
  },
  {
    "path": "libs/angular-i18next/src/.eslintrc.json",
    "chars": 73,
    "preview": "{\n  \"extends\": [\"../../.eslintrc.json\"],\n  \"ignorePatterns\": [\"!**/*\"]\n}\n"
  },
  {
    "path": "libs/angular-i18next/src/index.ts",
    "chars": 29,
    "preview": "export * from './lib/index';\n"
  },
  {
    "path": "libs/angular-i18next/src/lib/I18NextErrorHandlingStrategies.ts",
    "chars": 1017,
    "preview": "import { I18NextLoadResult } from './I18NextLoadResult';\nimport * as i18n from 'i18next';\n\nexport interface I18NextError"
  },
  {
    "path": "libs/angular-i18next/src/lib/I18NextEvents.ts",
    "chars": 593,
    "preview": "import { BehaviorSubject, Subject } from 'rxjs';\n\nimport {\n  ITranslationEvents,\n  MissingKeyEvent,\n  ResourceEvent,\n} f"
  },
  {
    "path": "libs/angular-i18next/src/lib/I18NextLoadResult.ts",
    "chars": 67,
    "preview": "export interface I18NextLoadResult {\n  err: any;\n  t?: Function;\n}\n"
  },
  {
    "path": "libs/angular-i18next/src/lib/I18NextModuleParams.ts",
    "chars": 223,
    "preview": "import { Type } from '@angular/core';\nimport { I18NextErrorHandlingStrategy } from './I18NextErrorHandlingStrategies';\n\n"
  },
  {
    "path": "libs/angular-i18next/src/lib/index.ts",
    "chars": 719,
    "preview": "export * from './I18NextErrorHandlingStrategies';\nexport * from './I18NextEvents';\nexport * from './I18NextLoadResult';\n"
  },
  {
    "path": "libs/angular-i18next/src/lib/interpolation.ts",
    "chars": 1187,
    "preview": "import type { FormatFunction, InterpolationOptions } from \"i18next\";\n\nexport function defaultInterpolationFormat(\n    va"
  },
  {
    "path": "libs/angular-i18next/src/lib/models.ts",
    "chars": 436,
    "preview": "import type * as i18n from 'i18next';\n\nexport type FormatPipeOptions = { format?: string; lng?: string; case?: string; ["
  },
  {
    "path": "libs/angular-i18next/src/lib/module.ts",
    "chars": 3544,
    "preview": "import { NgModule, ModuleWithProviders } from \"@angular/core\";\nimport { FormatFunction, InterpolationOptions } from \"i18"
  },
  {
    "path": "libs/angular-i18next/src/lib/namespace.resolver.ts",
    "chars": 609,
    "preview": "import { inject } from \"@angular/core\";\nimport { I18NEXT_SERVICE } from \"./tokens\";\nimport { ITranslationService } from "
  },
  {
    "path": "libs/angular-i18next/src/lib/namespaces.guard.ts",
    "chars": 629,
    "preview": "import { inject } from \"@angular/core\";\nimport { I18NEXT_SERVICE } from \"./tokens\";\n\n/**\n * This function can trigger th"
  },
  {
    "path": "libs/angular-i18next/src/lib/pipes/i18next-cap.pipe.ts",
    "chars": 889,
    "preview": "import { Inject, Injectable, Pipe, PipeTransform } from '@angular/core';\nimport {\n  I18NEXT_NAMESPACE,\n  I18NEXT_SCOPE,\n"
  },
  {
    "path": "libs/angular-i18next/src/lib/pipes/i18next-eager.pipe.ts",
    "chars": 1765,
    "preview": "import {\n  ChangeDetectorRef,\n  Inject,\n  Pipe,\n  PipeTransform\n} from '@angular/core';\nimport { takeUntilDestroyed } fr"
  },
  {
    "path": "libs/angular-i18next/src/lib/pipes/i18next-format.pipe.ts",
    "chars": 718,
    "preview": "import { Inject, Injectable, Pipe, PipeTransform } from '@angular/core';\nimport { FormatPipeOptions } from '../models';\n"
  },
  {
    "path": "libs/angular-i18next/src/lib/pipes/i18next.pipe.ts",
    "chars": 3411,
    "preview": "import { Inject, Injectable, Pipe, PipeTransform } from '@angular/core';\nimport { PipeOptions } from '../models';\nimport"
  },
  {
    "path": "libs/angular-i18next/src/lib/provider.ts",
    "chars": 3975,
    "preview": "import {\n  ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID,\n  EnvironmentProviders,\n  inject,\n  LOCALE_ID,\n  makeEnvironmentProv"
  },
  {
    "path": "libs/angular-i18next/src/lib/provider.utils.ts",
    "chars": 627,
    "preview": "import { Provider } from \"@angular/core\";\n\n/**\n * A feature for use when configuring `provideI18Next`.\n *\n * @publicApi\n"
  },
  {
    "path": "libs/angular-i18next/src/lib/services/i18next-title.ts",
    "chars": 554,
    "preview": "import { DOCUMENT } from '@angular/common';\nimport { Inject, Injectable } from '@angular/core';\nimport { Title } from '@"
  },
  {
    "path": "libs/angular-i18next/src/lib/services/i18next.service.ts",
    "chars": 7679,
    "preview": "import { Inject, Injectable, Optional } from '@angular/core';\nimport * as i18n from 'i18next';\nimport { I18NextErrorHand"
  },
  {
    "path": "libs/angular-i18next/src/lib/services/translation.events.ts",
    "chars": 544,
    "preview": "import { BehaviorSubject, Subject } from 'rxjs';\nimport * as i18n from 'i18next';\n\nexport type ResourceEvent = { lng: an"
  },
  {
    "path": "libs/angular-i18next/src/lib/services/translation.service.ts",
    "chars": 2837,
    "preview": "import * as i18n from 'i18next';\nimport { I18NextLoadResult } from '../I18NextLoadResult';\nimport { ITranslationEvents }"
  },
  {
    "path": "libs/angular-i18next/src/lib/tokens.ts",
    "chars": 900,
    "preview": "import * as i18n from 'i18next'\nimport { InjectionToken } from '@angular/core';\nimport { I18NextErrorHandlingStrategy } "
  },
  {
    "path": "libs/angular-i18next/src/test-setup.ts",
    "chars": 329,
    "preview": "import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless';\nimport { TestBed } from \"@angular/core/te"
  },
  {
    "path": "libs/angular-i18next/src/tests/module/module.spec.ts",
    "chars": 3454,
    "preview": "import { TestBed } from '@angular/core/testing';\nimport {\n  defaultInterpolationFormat,\n  I18NEXT_NAMESPACE,\n  I18NEXT_N"
  },
  {
    "path": "libs/angular-i18next/src/tests/pipes/I18NextEagerPipe.spec.ts",
    "chars": 5678,
    "preview": "import { ApplicationInitStatus, ChangeDetectorRef } from '@angular/core';\nimport { TestBed } from '@angular/core/testing"
  },
  {
    "path": "libs/angular-i18next/src/tests/pipes/I18NextPipe.spec.ts",
    "chars": 5864,
    "preview": "import { ApplicationInitStatus } from '@angular/core';\nimport { TestBed } from '@angular/core/testing';\nimport { I18Next"
  },
  {
    "path": "libs/angular-i18next/src/tests/projectTests/project.component.ts",
    "chars": 323,
    "preview": "import { Component, ViewEncapsulation } from '@angular/core';\nimport { I18NextPipe } from '../../lib';\n\n@Component({\n  s"
  },
  {
    "path": "libs/angular-i18next/src/tests/projectTests/projectTests.spec.ts",
    "chars": 524,
    "preview": "import { TestBed } from \"@angular/core/testing\";\nimport { MOCK_I18N_PROVIDERS } from \"../setup\";\nimport { ProjectCompone"
  },
  {
    "path": "libs/angular-i18next/src/tests/provider/provider.spec.ts",
    "chars": 3736,
    "preview": "import { ChangeDetectorRef, inject, provideAppInitializer } from '@angular/core';\nimport { TestBed } from '@angular/core"
  },
  {
    "path": "libs/angular-i18next/src/tests/service/I18NextService.spec.ts",
    "chars": 3439,
    "preview": "import { TestBed } from '@angular/core/testing';\nimport {\n  I18NextModule, I18NEXT_SERVICE,\n  ITranslationService\n} from"
  },
  {
    "path": "libs/angular-i18next/src/tests/setup.ts",
    "chars": 245,
    "preview": "import { provideI18NextMockAppInitializer, withMock } from '../../testing/src/public_api';\nimport {\n  provideI18Next,\n\n}"
  },
  {
    "path": "libs/angular-i18next/ssr/ng-package.json",
    "chars": 3,
    "preview": "{}\n"
  },
  {
    "path": "libs/angular-i18next/ssr/src/provider.ssr.ts",
    "chars": 679,
    "preview": "import { REQUEST_CONTEXT } from '@angular/core';\nimport { I18NEXT_INSTANCE, I18NextFeature, I18NextFeatureKind, makeI18N"
  },
  {
    "path": "libs/angular-i18next/ssr/src/public_api.ts",
    "chars": 32,
    "preview": "export * from './provider.ssr';\n"
  },
  {
    "path": "libs/angular-i18next/testing/ng-package.json",
    "chars": 3,
    "preview": "{}\n"
  },
  {
    "path": "libs/angular-i18next/testing/src/mock.service.ts",
    "chars": 4513,
    "preview": "import { Injectable } from '@angular/core';\nimport { jest } from '@jest/globals';\nimport { defaultInterpolationFormat, I"
  },
  {
    "path": "libs/angular-i18next/testing/src/provider.ts",
    "chars": 1188,
    "preview": "import { inject, provideAppInitializer } from '@angular/core';\nimport { I18NEXT_SERVICE, I18NextFeature, I18NextFeatureK"
  },
  {
    "path": "libs/angular-i18next/testing/src/public_api.ts",
    "chars": 60,
    "preview": "export * from './mock.service';\nexport * from './provider';\n"
  },
  {
    "path": "libs/angular-i18next/tsconfig.json",
    "chars": 688,
    "preview": "\n{\n  \"extends\": \"../../tsconfig.base.json\",\n  \"compilerOptions\": {\n    \"target\": \"es2022\",\n    \"forceConsistentCasingInF"
  },
  {
    "path": "libs/angular-i18next/tsconfig.lib.json",
    "chars": 310,
    "preview": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"../dist/out-tsc\",\n    \"declaration\": true,\n    \""
  },
  {
    "path": "libs/angular-i18next/tsconfig.spec.json",
    "chars": 421,
    "preview": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"../dist/out-tsc\",\n    \"module\": \"preserve\",\n    "
  },
  {
    "path": "migrations.json",
    "chars": 2794,
    "preview": "{\n  \"migrations\": [\n    {\n      \"version\": \"22.0.0-beta.1\",\n      \"description\": \"Updates release version config based o"
  },
  {
    "path": "nx.json",
    "chars": 1626,
    "preview": "{\n  \"affected\": {\n    \"defaultBase\": \"master\"\n  },\n  \"targetDependencies\": {\n    \"build\": [\n      {\n        \"target\": \"b"
  },
  {
    "path": "package.json",
    "chars": 4266,
    "preview": "{\n  \"name\": \"angular-i18next-rep\",\n  \"version\": \"0.0.1\",\n  \"author\": {\n    \"name\": \"Sergey Romanchuk\"\n  },\n  \"homepage\":"
  },
  {
    "path": "tools/tsconfig.tools.json",
    "chars": 251,
    "preview": "{\n  \"extends\": \"../tsconfig.base.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"../dist/out-tsc/tools\",\n    \"rootDir\": \".\""
  },
  {
    "path": "tsconfig.base.json",
    "chars": 1101,
    "preview": "{\n  \"compileOnSave\": false,\n  \"compilerOptions\": {\n    \"rootDir\": \".\",\n    \"sourceMap\": true,\n    \"declaration\": false,\n"
  },
  {
    "path": "tsconfig.json",
    "chars": 40,
    "preview": "{\n  \"extends\": \"./tsconfig.base.json\"\n}\n"
  }
]

About this extraction

This page contains the full source code of the Romanchuk/angular-i18next GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 137 files (219.4 KB), approximately 66.5k tokens, and a symbol index with 181 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!