Skip to content

[i18n] support i18n-localiza in template expression. #48229

Open

Description

@Gaubee

Which @angular/* package(s) are relevant/related to the feature request?

compiler, localize

Description

example:

<span> {{ nikename ?? 'ano' }} </span>

for now to support i18n, I should rewrite those code:

<span> {{ nickname ?? $$ANO }} </span>
class MyComponent {
  readonly $$ANO = $localiza`:@@ANO:ano`;
}

or like:

<span>
  <ng-container *ngIf="nickname;else Str">{{nickname}}</ng-container>
  <ng-template #Str><ng-container i18n="@@ANO">ano</ng-container></ng-template>
<span>

Proposed solution

<span> {{ nikename ?? $localiza(':@@ANO:ano') }} </span>

after read the source code. I think inject an function for template is better choose then pipe.

those code will compile to on consts:

$localiza.trans[":@@ANO:ano"] = $localiza`:@@ANO:ano`

Tips

  1. $localize only support string literal.

    eg: $localize(str_var) is no support, suppose use variable, why not str_var = $localize`text`

  2. $localize only support one argument.

    suppose transform code:

    $localize(':@@MY_INTRO:my name is ', username, ', I\'m ', age ,' years old.')

    to

    $localize`:@@MY_INTRO:my name is \${name}, I'm ${age} years old.`;

    consider using html would get better readability and maintainability:

    <ng-container i18n="@@MY_INTRO">
      my name is {{name}}, I'm {{age}} years old.
    </ng-container>

Alternatives considered

noop

For now. I'm trying to make those code work in my project by hacking the source code. I hope someone could give me better suggest.

Relational link

Activity

  1. changed the title [-][i18n] support i18n pipe line in template expression.[/-] [+][i18n] support i18n-localiza in template expression.[/+] on Nov 25, 2022
  2. Gaubee commented on Nov 26, 2022

    @Gaubee
    Author

    I have implemented this proposal initially, I know it is not an easy work, but it can be implemented quickly with a simple hack, now I will use it for my project, here is my commit:Gaubee@81f89bd

    the compile will be like:

    image

    the 'hello' define in messages.**.xlf:

          <trans-unit id="HI" datatype="html">
            <source>hello</source>
            <context-group purpose="location">
              <context context-type="sourcefile">src/app/app.component.ts</context>
              <context context-type="linenumber">12</context>
            </context-group>
          </trans-unit>
  3. Gaubee commented on Nov 26, 2022

    @Gaubee
    Author

    I have implemented this proposal initially, I know it is not an easy work, but it can be implemented quickly with a simple hack, now I will use it for my project, here is my commit:Gaubee@81f89bd

    Here is my Angular project code:

    app.component.html

    <p>{{ $localize(':@@HI:Hi~') }}</p>

    app.component.ts

    import { Component } from '@angular/core';
    
    const trans: Record<string, string> = Object.create(null);
    Object.defineProperty($localize, 'trans', { value: trans, writable: false });
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css'],
    })
    export class AppComponent {
      $localize = (key: string) => {
        return trans[key] ?? key.match(/^:.*:(.*)/)?.[1] ?? key;
      };
    }

    just like import {loadTranslations} from '@angular/localize';

  4. Gaubee commented on Nov 26, 2022

    @Gaubee
    Author

    ng extract-i18n works too. because it follows the principle of extraction: https://github.com/angular/angular/blob/main/packages/localize/tools/src/extract/extraction.ts#L49

  5. reopened this on Nov 26, 2022
  6. Gaubee commented on Nov 26, 2022

    @Gaubee
    Author

    I have implemented this proposal initially, I know it is not an easy work, but it can be implemented quickly with a simple hack, now I will use it for my project, here is my commit:Gaubee@81f89bd

    If some one when to try this. inject codes to node_modules/@angular/compiler/fesm2015/compiler.mjs :

    ps: you can use pnpm patch @angular/compiler or patch-package

    search error('Text nodes should be interpolated and never bound directly.');, and you can found it in visitBoundText method.

    then insert this code at the end of the function

            // #region @gaubee
            const addI18nLocalize = (args) => {
                var _a;
                let i18n_meta_and_text = '';
                // const messageParts: o.LiteralPiece[] = [];
                // const placeHolders: o.PlaceholderPiece[] = [];
                // const expressions: o.Expression[] = [];
                // let sourceSpan: ParseSourceSpan | null = null;
                const template_string_elements = [];
                const template_string_expressions = [];
                if (args.length !== 1) {
                    error('$localize in interpolation only support single argument.');
                }
                for (const arg of args) {
                    const convertedPropertyBinding = convertPropertyBinding(this, this.getImplicitReceiverExpr(), arg, this.bindingContext());
                    const currValExpr = convertedPropertyBinding.currValExpr;
                    if (currValExpr instanceof LiteralExpr && typeof currValExpr.value === 'string') {
                        // messageParts.push(messageParts)
                        template_string_elements.push(new TemplateLiteralElement(currValExpr.value, (_a = currValExpr.sourceSpan) !== null && _a !== void 0 ? _a : undefined, undefined));
                        i18n_meta_and_text += currValExpr.value;
                    }
                    else {
                        // template_string_expressions.push(currValExpr);
                        error('in interpolation, $localize\'s argument must by string literal.');
                    }
                }
                // const i18n_meta = i18n_meta_and_text.match(/^:(.*?):/)?.[1] ?? '';
                // const i18n_text = i18n_meta_and_text.match(/^:.*?:(.*)/)?.[1] ?? i18n_meta_and_text;
                // const message = parseI18nMeta(i18n_meta);
                // const localizedString = o.localizedString(
                //   message,
                //   messageParts,
                //   placeHolders,
                //   expressions,
                //   sourceSpan
                // );
                const taggedTemplate$1 = taggedTemplate(new ReadVarExpr('$localize', FUNCTION_TYPE, null), new TemplateLiteral(template_string_elements, template_string_expressions));
                const saveLocalizeToGlobal = new WriteVarExpr(`$localize.trans[${JSON.stringify(i18n_meta_and_text)}]`, taggedTemplate$1, null);
                this._constants.constExpressions.push(saveLocalizeToGlobal);
            };
            class I18nConverter extends AstMemoryEfficientTransformer {
                visitCall(ast, context) {
                    if (ast.receiver instanceof PropertyRead && ast.receiver.name === '$localize') {
                        addI18nLocalize(ast.args);
                    }
                    return super.visitCall(ast, context);
                }
            }
            text.value.visit(new I18nConverter());
            // #endregion

    look like:
    image

  7. added
    area: i18nIssues related to localization and internationalization
    on Nov 28, 2022
  8. added this to the needsTriage milestone on Nov 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: i18nIssues related to localization and internationalization

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      Sponsor
      SponsoredKunjungi sekarang
      Promo