vokeit-oss / ng-i18n-aot-loader

angular i18n loader to support AOT with dynamic locale changes
9 stars 1 forks source link

Is there a working prototype? Is there a complete example or demo available? #1

Closed gregbown closed 7 years ago

gregbown commented 7 years ago

I extracted my strings to xlif2 files using the Angular CLI as defined here Angular docs My npn script: "i18n": "./node_modules/.bin/ng-xi18n --i18nFormat=xlf2 --outFile=meta/i18n/en/messages.xlf", I have a meta directory as indicated in my script above to which I added /i18n/de/messages.xlf I have tried your outlined approach with multiple variations in the loader configuration to no avail. { enforce: 'pre', test: /\.html$/, use: [ { loader: '@actra-development-oss/ng-i18n-aot-loader', options: { enabled: true, translationFiles: ['../meta/i18n/de/messages.xlf'], translationFormat: 'xlf2' } } ], include: ['../meta/i18n'], exclude: ['/src/index.html'] }, The module is imported into my shared module, and builds fine but on load the current locale is always default-$$$-locale I am using AOT compilation. Windows 10, Chrome Version 56.0.2924.87, Angular 4.3.5, Typescript 2.5.1, Webpack 3.5.5, Node 6.10.2, npm 4.0.2 Any advice is appreciated Thank you Greg

actra-gschuster commented 7 years ago

I'll setup a demo later this day.

It's correct so far that the service map is empty and you're receiving default-$$$-locale - the service does not read translations, it's only there for switching the locale. The actual translations are rendered inside your HTML, to check if they work either check your build files or add a service call that changes the locale to, by your example, e.g. "de" after loading the app, you should then see your translated text.

actra-gschuster commented 7 years ago

I created a separate repo for the demo, it's based on a plain project created by ng new.

Repo: https://github.com/actra-development-oss/ng-i18n-aot-demo Demo: https://actra-development-oss.github.io/ng-i18n-aot-demo/

I also had time to read your comment more carefully... The loader config is wrong, "include" should be the base path where your HTML files live - or simply don't set "include". "exclude" seems also to be wrong, you're specifying an absolute path "/src" - is this correct or did you mean a relative path "./src" ?

gregbown commented 7 years ago

@actra-gschuster Thank you very much for the prompt response and excellent work. I just want to add a few details for anyone else seeking to use this.

my config Using the setting translationFormat: 'xliff2' will allow you to use the the xliff version 2.0, that is an option with the Angular i18n extraction tool Also, for some reason the loader would always try to parse my index.html unless I provided an absolute path as can be seen in my exclude settings below. { enforce: 'pre', test: /\.html$/, use: [ { loader: '@actra-development-oss/ng-i18n-aot-loader', options: { enabled: true, translationFiles: ['./src/i18n/messages.de_DE.xlf'], translationFormat: 'xliff2' } } ], exclude: [ path.join(process.cwd(), "template\\index.html") ] },

This a great alternative to the awfully complex method of shipping a separate application for every locale.

Thank you again Greg

actra-gschuster commented 7 years ago

include and exclude are options of webpack, not the loader specifically, so relative paths depend on the location of the actual webpack config file. To circumvent any path related problems I always use absolute paths.

Thanks for your comments and hints :-)

gregbown commented 6 years ago

I notice that even though I am including the i18n module in my shared module as described in the readme for ng-i18n-aot-loader with lazy loaded modules importing the shared module; I still need to have an observable set up to get the locale. despite having already called setLocale elsewhere. Reason being each module that imports the shared module creates an instance and thus each module needs to call setLocale.

I suppose this could be eliminated if there was a way to create a singleton i18n service and pass it into the i18n modules constructor.

Also, using the ag-grid framework, its reference to the grid becomes null when the ng-template is changed. I am unsure how to restore the bindings once it fails so the only solution is to re load the page with the locale persisted in a cookie. Not terrible, just less than ideal.

Thank you again for this library, it is still far better than compiling a separate app for each locale.

actra-gschuster commented 6 years ago

Do you import the module with forRoot() in your root module and WITHOUT forRoot() in your shared module? That way there should be only one instance of the service in the root injector.

For your ag-grid problem, please show some code for me to check.

gregbown commented 6 years ago

Ah, not sure why that never occurred to me as a viable solution. Thank you or pointing that out. Separating localized markup into unique components seems to alleviate the grid issue as it appeared the grid was getting lumped into an ng-container for a localized string in the markup above it.

The only issue I am seeing now is where I have a shared pagination component that appears twice on one view, top and bottom of page, only the last instance will change locales unless I do a page reload.

Thank you for the tip on forRoot again

actra-gschuster commented 6 years ago

For i18n contents that are used multiple times within a template try using <ng-template> to "declare" them and <ng-container> to output them where needed.

General "problem" with the approach this module takes is that all i18n contents have to be wrapped inside <ng-container> in order to be able to switch locales at runtime. This triggers re-insertion of external components like ag-grid but is by design (of angular), as the contents need to be rendered in-time.

gregbown commented 6 years ago

Tried this but no success. Still only the lower one switches locale. Am I missing something?

            <div class="social-and-pagination row justify-content-between">
                <social-links [shareLink]="shareLink$ | async" class="medium dark label-inline"></social-links>
                <ng-container *ngTemplateOutlet="pagination"></ng-container>
            </div>
            <div affixChild=".ag-header" affixHeader="nav-component > header">
                <ag-grid-angular
                    #agGrid
                    class="ag-op"
                    [gridOptions]="LBGO"
                    style="width: 100%;">
                </ag-grid-angular>
            </div>
            <div class="social-and-pagination row justify-content-end">
                <ng-container *ngTemplateOutlet="pagination"></ng-container>
            </div>
        </div>
    </div>
</div>
<ng-template #pagination>
    <div class="pagination-box">
        <pagination-control
            [rows]="rowConfig$ | async"
            [pages]="pageConfig$ | async"
            (changed)="rowsPerPageChanged($event)"
            (first)="goToFirstPage($event)"
            (prev)="goToPrevious($event)"
            (next)="goToNext($event)"
            (last)="goToLastPage($event)"
            (goto)="goToPage($event)">
        </pagination-control>
    </div>
</ng-template>

Result image

actra-gschuster commented 6 years ago

How does the HTML of pagonation-control look like? Can't see any i18n in the code you've shown.

gregbown commented 6 years ago

Thanks for the quick response, here is a gist

The pagination component is declared and exported in shared module, shared module is imported everywhere.

I have removed all instances of i18n in the template that contains the grid due to the aforementioned issue, toggling locale breaks the grid.