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

feat: config import translations #602

Merged
merged 34 commits into from
Jun 5, 2024
Merged

Conversation

tomasciccola
Copy link
Contributor

May fix #587

@tomasciccola tomasciccola self-assigned this May 2, 2024
@tomasciccola
Copy link
Contributor Author

I've basically followed the pattern with the *fields and *presetiterators.
I had to change the preset iterator so that it also yields its name (which is actually the object key) since that is used in translations to reference it (so translations don't use the name field in presets but the key itself; i.e. "Boundary Line" is actually "boundary-line")

@tomasciccola tomasciccola marked this pull request as ready for review May 9, 2024 15:51
@tomasciccola
Copy link
Contributor Author

Also, keen to read alternatives to having 4 nested loops. Using a reducer is an alternative but I don't think it'll necessarily be clearer code...

@tomasciccola
Copy link
Contributor Author

there's a type of translation thats always empty called category. I don't know the use of it, but I'm skipping it - for now - and only importing 'fields' and 'presets'. We may want to update the default config (and config builder) in the future to remove this..

@EvanHahn EvanHahn removed the request for review from gmaclennan May 13, 2024 14:11
Copy link
Contributor

@EvanHahn EvanHahn left a comment

Choose a reason for hiding this comment

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

I wonder if you could avoid the nested loops by defining functions that make it clearer what each step does. Something like:

for (let [lang, languageTranslations] of Object.entries(
  translationsFile
)) {
  yield* translationsForLanguage(lang, languageTranslations)
}

// ...

/**
 * @param {string} lang
 * @param {Record<ValidDocTypes, unknown>} languageTranslations
 * @returns {Iterable<{name: string, value:import('@mapeo/schema').TranslationValue}>}
 */
function* translationsForLanguage(lang, languageTranslations) {
  const { language: languageCode, region: regionCode } = parseBCP47(lang)
  if (!languageCode) throw new Error(`invalid translation language`)
  for (let [schemaNameRef, languageTranslationsForDocType] of Object.entries(
    languageTranslations
  )) {
    // TODO: Define this function...
    yield* translationsForDocType(
      languageCode,
      regionCode,
      schemaNameRef,
      languageTranslationsForDocType
    )
  }
}

Might be clearer? Not sure.

there's a type of translation thats always empty called category. I don't know the use of it, but I'm skipping it - for now - and only importing 'fields' and 'presets'. We may want to update the default config (and config builder) in the future to remove this..

I agree that it'd be useful to remove this from the config.

tests/config-import.js Outdated Show resolved Hide resolved
tests/config-import.js Outdated Show resolved Hide resolved
src/mapeo-project.js Show resolved Hide resolved
)) {
if (!isRecord(translationByDocType))
throw new Error('invalid translation docType object')
if (!(docType === 'presets' || docType === 'fields')) continue
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we error or warn in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we should warn but maybe after we update the default config so that it doesn't have categories (I can leave a TODO comment that addressed this, but for know we just continue?)

src/config-import.js Outdated Show resolved Hide resolved
src/config-import.js Outdated Show resolved Hide resolved
src/config-import.js Show resolved Hide resolved
throw new Error(`invalid translation field`)
for (let [fieldRef, message] of Object.entries(fieldsToTranslate)) {
// we skip messages that aren't strings, for now
if (isRecord(message)) continue
Copy link
Contributor

Choose a reason for hiding this comment

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

When would message ever be a record?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It shouldn't actually, it was a misconception I had based on badly parsing the file...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, it wasn't a misconception, there are actually some messages that are objects. This happens when we are translating an options in a Field.
Options should be a {label:string, value:string|boolean|number|null}[].
This is imposed on mapeo/schema.
Recently we updated the default config since options was only a string[] (see here for some examples). The thing is that we didn't updated the translations accordingly.
So, I think what should happen is:

  • We should update translations of options in fields in the default config in the shape of {label: translatedValue, value: translatedValue}
  • When message is an object, then we should iterate over it and produce a translation with fieldRef of options[idx].label and options[idx].value for every option (following dot-prop notation).

I think this makes sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Makes sense to me!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

PR updating translation options in fields -> digidem/mapeo-default-config#58

src/config-import.js Outdated Show resolved Hide resolved
Tomás Ciccola added 4 commits May 14, 2024 11:46
1. split *translation into individual functions (to avoid nested loops)
2. remove `parseTranslationsFile`
3. return value without `docIdRef` (since we don't know it at that
   point)
4. move warnings outside of `readConfig` (to have it accessible in
   outside functions)
5. push to warnings instead of throwing errors
6. remove unnecessary check of `message` being an object
@EvanHahn
Copy link
Contributor

Let me know when I should take another look at this (no rush).

Copy link
Contributor

@EvanHahn EvanHahn left a comment

Choose a reason for hiding this comment

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

LGTM other than my small comments.

Comment on lines 33 to 34
/** @type {Error[]} */
let warnings = []
Copy link
Contributor

Choose a reason for hiding this comment

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

I think I see why this got hoisted, but I don't love that this is a global. Is there a way to avoid doing something like this? Maybe by moving translationsForLanguage into *translations()?

Copy link
Contributor

Choose a reason for hiding this comment

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

If two configs are imported at the same time, these warnings will get squashed because readConfig is asynchronous. Could we move this "back in" to readConfig?

src/config-import.js Outdated Show resolved Hide resolved
src/config-import.js Outdated Show resolved Hide resolved
for (const [schemaNameRef, languageTranslationsForDocType] of Object.entries(
languageTranslations
)) {
// TODO: push to warnings when removing categories from default config
Copy link
Contributor

Choose a reason for hiding this comment

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

What does this mean? And should we actually add to warnings in this case, uncommenting line 287?

src/config-import.js Outdated Show resolved Hide resolved
src/config-import.js Outdated Show resolved Hide resolved
tests/config-import.js Outdated Show resolved Hide resolved
Tomás Ciccola and others added 6 commits May 30, 2024 12:30
Copy link
Contributor

@EvanHahn EvanHahn left a comment

Choose a reason for hiding this comment

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

LGTM other than my small comment. Happy to pair if helpful.

Comment on lines 33 to 34
/** @type {Error[]} */
let warnings = []
Copy link
Contributor

Choose a reason for hiding this comment

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

If two configs are imported at the same time, these warnings will get squashed because readConfig is asynchronous. Could we move this "back in" to readConfig?

Copy link
Contributor

@EvanHahn EvanHahn left a comment

Choose a reason for hiding this comment

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

LGTM! One small non-blocking question.

schemaNameRef,
languageTranslationsForDocType,
] of Object.entries(languageTranslations)) {
// TODO: remove categories check when removed from default config
Copy link
Contributor

Choose a reason for hiding this comment

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

This is still necessary even with the new default config, right?

@tomasciccola tomasciccola merged commit eeb87af into main Jun 5, 2024
7 checks passed
@tomasciccola tomasciccola deleted the feat/configImportTranslations branch June 5, 2024 18:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Import translations when importing a configuration
2 participants