Skip to content

DEV: Add a Translation component. #33082

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

pento
Copy link
Member

@pento pento commented Jun 5, 2025

✨ What's This?

Reference: t/155601

A new <Translation> component for interpolating components into translatable strings.

This PR also includes a little bit of refactoring in the i18n library to make it easier to hook into.

Example:

// "some.translation.key" = "Welcome, %{username}! The date is %{shortdate}!"
<Translation
  @scope="some.translation.key"
  @options={{hash shortdate=shortDate}}
>
  <:placeholders as |Placeholder|>
    <Placeholder @name="username">
      <UserLink @user={{user}}>{{user.username}}</UserLink>
    </Placeholder>
  </:placeholders>
</Translation>

📺 Screenshots

image

👑 Testing

Review the new integration tests, consider any missing cases that should be covered.

@pento pento self-assigned this Jun 5, 2025
@pento pento marked this pull request as ready for review June 11, 2025 04:59
@github-actions github-actions bot added the i18n PRs which update English locale files or i18n related code label Jun 11, 2025
Comment on lines +44 to +65
_placeholderKeys = new Map();

/**
* A map of placeholder keys to their corresponding DOM elements.
*
* @type {Map<String, HTMLElement>}
*/
_placeholderElements = new Map();

/**
* A map of placeholder keys to their appearance in the translation string.
*
* @type {Map<String, String>}
*/
_placeholderAppearance = new Map();

/**
* Tracks which placeholders have been rendered.
*
* @type {Array<String>}
*/
_renderedPlaceholders = [];
Copy link
Member

@davidtaylorhq davidtaylorhq Jun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think having all this state stored on the component instance will lead to a lot of possible bugs. e.g. if the component is rendered with one @scope, and then the @scope value changes, then the getter will be automatically re-run. But there is nothing to clear all this internal state, so we'll end up with duplicate data in these sets/arrays.

As a general rule, we should aim to avoid any kind of side-effects in getters.

I think I'd aim to build everything you need inside the getter, and then return it. So something like:

get textAndPlaceholders(){
  ...
  return {
    parts: ...,
    elements: ...,
    ...
  }
}

And then

<template>
  {{#let this.textAndPlaceholders as |infos|}}
    {{#each infos.parts as |part|}}
      {{part}}
    {{/each}}

It's probably worth having a test for that case where the scope and/or opts arguments change at runtime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
i18n PRs which update English locale files or i18n related code
Development

Successfully merging this pull request may close these issues.

3 participants