Skip to content
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

Support properties on (linked) translations #275

Open
domyd opened this issue Dec 27, 2024 · 2 comments
Open

Support properties on (linked) translations #275

domyd opened this issue Dec 27, 2024 · 2 comments
Labels
enhancement New feature or request

Comments

@domyd
Copy link

domyd commented Dec 27, 2024

Motivation

Hello!

In our project we use certain domain words that need to be able to be swapped out; for example domain.workOrder here:

domain:
  workOrder: "Work Order"
noTasks: "This @:{domain.workOrder} has no tasks."

I can use linked translations to reuse these and LocaleSettings.overrideTranslationsFromMap to swap them out as needed, so far so good, love that.

But then I add German translations and now the parent translation depends on the linked translation's grammatical gender:

# domain.workOrder is "Auftrag", which has a masculine gender -> "Dieser Auftrag"
noTasks: "Dieser Auftrag hat keine Positionen."
# domain.workOrder is "Aufgabe", which has a feminine gender -> "Diese Aufgabe"
noTasks: "Diese Aufgabe hat keine Positionen."

I would like to have the possibility to adapt the parent (noTasks) translation based on a property of the linked (domain.workOrder) value. I think that would solve this problem in a fairly elegant way and probably enable some other use cases that I didn't think of (yet). I sketched out a rough draft of what this could look like below.

Is this something that we could add? I would be willing to implement this, if it helps 🙂

Developer Experience
An example of what this feature could look like:

# Config

# Enumerate a set of properties and its values per locale
properties:
  de:
    article:
      - der
      - die
      - das
# de.i18n.yaml

domain:
  # Specify the property's value on a translation.
  workOrder: "Auftrag{article=der}"
noTasks:
  # When using a linked translation with an attached property, provide 
  # variants for each of the property's values.
  der: "Dieser @:{domain.workOrder} hat keine Positionen."
  die: "Diese @:{domain.workOrder} hat keine Positionen."
  das: "Dieses @:{domain.workOrder} hat keine Positionen."
print(t.noTasks); // prints "Dieser Auftrag hat keine Positionen.

await LocaleSettings.overrideTranslationsFromMap(
  locale: AppLocale.de,
  isFlatMap: false,
  map: {
    'domain': {
      'workOrder': 'Aufgabe{article=die}'
    }
  },
);

print(t.noTasks); // prints "Diese Aufgabe hat keine Positionen."

// When accessing `domain.workOrder` directly, the property is stripped away
print(t.domain.workOrder) // prints 'Aufgabe'

Importantly, this needs to be locale-specific. In English, the above would look like this:

# Config

properties:
  # Still keeping around the `article` property for the German translations.
  de:
    article:
      - der
      - die
      - das
# en.i18n.yaml

domain:
  workOrder: "Work Order"
noTasks: "This @:{domain.workOrder} has no tasks."

But I imagine there might be a use-case for non-locale-specific properties as well? Those could be listed under something like properties.global if need be.

@domyd domyd added the enhancement New feature or request label Dec 27, 2024
@Tienisto
Copy link
Member

Thanks for the pull request!

At first glance, this feature is quite complex.

workOrder: "Auftrag{article=der}"

The given example implies, that you can have multiple properties of the same type.
I would prefer to have workOrder(article=der): "Auftrag" making it bound to the entry.
Another problem is that noTasks can only have one "property", otherwise you would need to add nesting (which doesn't exist in slang yet).

In general, I would advice you to use the enums feature (https://pub.dev/packages/slang#-custom-contexts--enums) to implement this logic. For example:

workType(context=I18nWorkType):
  task: "Aufgabe"
  order:  "Auftrag"
  general: "" # not used in German
noTasks(context=I18nWorkType):
  task: "Diese @:workType hat keine Positionen."
  order: "Dieser @:workType hat keine Positionen."
  general: "" # not used in German
I18nWorkType workType = I18nWorkType.order;

print(t.noTasks(context: workType)); // prints "Dieser Auftrag hat keine Positionen.

workType = I18nWorkType.task;

print(t.noTasks(context: workType)); // prints "Diese Aufgabe hat keine Positionen."

The downside is that you move i18n logic into business logic space. However, I think that LocaleSettings.overrideTranslationsFromMap() is the same as workType = I18nWorkType.task;

The reason, I am hesitant is because this feature adds complexity of a novel feature that I've not seen in other i18n libraries.

@domyd
Copy link
Author

domyd commented Dec 30, 2024

Thanks for the response!

I could indeed solve this problem with enums, at the price of having the article passed in through an enum on every translation that uses domain.workOrder. But I also think that this really should be the responsibility of the i18n library. And yeah, I couldn't do this in any other i18n library that I know of either.

If we can agree that this is a legitimate use case, then I do think this is at least worth exploring.

The given example implies, that you can have multiple properties of the same type.

I imagined it as more of a bottom-up enum, where instead of values being passed down from the translation consumer, they are passed up from translation values and other translations may be the consumer. In that sense I didn't imagine domain.workOrder having two or more article values for example, just one, like an enum. So workOrder: "Auftrag{article=der,die}" wouldn't be allowed, and since multiple properties can't easily be done either, as you say, something like workOrder: "Auftrag{article=der}{num=singular}" would be illegal too. But these are ways in which this could be extended by in the future.

I would prefer to have workOrder(article=der): "Auftrag" making it bound to the entry.

I suppose that could also work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants