Skip to content

Comments

validator: field translator#630

Merged
bangarang merged 11 commits intomainfrom
feat/FieldTranslator
Oct 22, 2024
Merged

validator: field translator#630
bangarang merged 11 commits intomainfrom
feat/FieldTranslator

Conversation

@bangarang
Copy link
Collaborator

@bangarang bangarang commented Sep 24, 2024

translate

@bangarang bangarang force-pushed the feat/FieldTranslator branch from a411c45 to 2cd67c4 Compare October 17, 2024 04:28
@bangarang bangarang marked this pull request as ready for review October 17, 2024 04:37
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 17, 2024

Walkthrough

This pull request introduces the @flatfile/plugin-convert-translate, a plugin that integrates with the Google Translate API to facilitate field translations within Flatfile listeners. It includes comprehensive documentation, new configuration files for Jest and Rollup, and unit tests for the translation functionality. The plugin allows batch translations, configurable source and target languages, and error handling. The main implementation processes record translations, while the README provides installation instructions and usage examples.

Changes

File Change Summary
convert/translate/README.MD New documentation for the Flatfile Field Translation Plugin, detailing capabilities and usage.
convert/translate/jest.config.js New Jest configuration for testing in a Node environment, including setup files and timeout settings.
convert/translate/rollup.config.mjs New Rollup configuration file for managing the build process.
convert/translate/src/convert.translate.plugin.spec.ts New unit tests for the convertTranslatePlugin function, verifying integration with Google Translate API.
convert/translate/src/convert.translate.plugin.ts New plugin implementation for translating records, including configuration interface and core functions.
convert/translate/src/index.ts Export statement added to re-export entities from convert.translate.plugin.
.changeset/lovely-suits-begin.md Initial commit for the package @flatfile/plugin-convert-translate with a minor version update.

Possibly related PRs

  • validate: isbn #618: The main PR updates the documentation for the @flatfile/plugin-convert-translate plugin, which includes details about its functionality and configuration.
  • validator: string #650: The main PR introduces documentation for the @flatfile/plugin-convert-translate plugin, while PR validator: string #650 updates the documentation for the @flatfile/plugin-validate-string, enhancing clarity and usability.
  • chore: update changeset #667: The main PR focuses on the initial documentation for the @flatfile/plugin-convert-translate, and PR chore: update changeset #667 documents the initial launch of several plugins, including the @flatfile/plugin-validate-string.

Suggested reviewers

  • carlbrugger

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Outside diff range and nitpick comments (5)
convert/translate/src/convert.translate.plugin.spec.ts (2)

1-12: LGTM! Consider using a more specific mock response.

The imports and mocking setup look good. Mocking the Google Translate module is a great practice for unit testing.

Consider using a more specific mock response that aligns with your test cases. For example:

translate: jest.fn().mockResolvedValue([['Hola', 'Mundo'], ['Hello', 'World']]),

This way, you can test both the Spanish and English translations in your test cases.


30-46: Good test coverage. Consider adding a translation functionality test.

The existing test cases cover important aspects of the plugin's initialization and integration with the listener. They ensure that the Google Translate client is initialized correctly and that a record hook is added to the listener.

To improve test coverage, consider adding a test case that checks the actual translation functionality. For example:

it('should translate fields correctly', async () => {
  const mockRecord = {
    field1: 'Hello',
    field2: 'World',
  };

  const hook = convertTranslatePlugin(listener, config);
  const result = await hook(mockRecord);

  expect(result.field1).toBe('Hola');
  expect(result.field2).toBe('Mundo');
});

This test would verify that the plugin correctly translates the specified fields using the mocked Google Translate API.

convert/translate/src/convert.translate.plugin.ts (2)

55-56: Avoid logging errors directly to the console in production code

Logging errors to the console can expose sensitive information and is not recommended in production environments. Consider using a dedicated logging mechanism or removing the console statements.

Apply this diff to remove the console logging:

-    console.error('Error processing record:', error)

75-77: Remove unnecessary sheetSlug check

Within the record hook for config.sheetSlug, checking event.context.sheetSlug !== config.sheetSlug is redundant since the hook is already scoped to that sheet.

Apply this diff to remove the redundant check:

             // Check if this record is from the configured sheet
-            if (event.context.sheetSlug !== config.sheetSlug) {
-              return record
-            }
convert/translate/README.MD (1)

35-36: Recommend using environment variables for sensitive configurations

To enhance security and maintainability, consider using environment variables for projectId and keyFilename instead of hardcoding them. This approach prevents sensitive information from being exposed in the codebase and allows for easier configuration across different environments.

Apply this diff to implement the change:

 const translationConfig = {
   sourceLanguage: 'en',
   targetLanguage: 'es',
   sheetSlug: 'products',
   fieldsToTranslate: ['name', 'description'],
-  projectId: 'your-google-cloud-project-id',
-  keyFilename: '/path/to/your/google-cloud-key.json'
+  projectId: process.env.GOOGLE_CLOUD_PROJECT_ID,
+  keyFilename: process.env.GOOGLE_CLOUD_KEY_FILENAME
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between d7c7d7e and 2cd67c4.

⛔ Files ignored due to path filters (3)
  • convert/translate/metadata.json is excluded by !**/*.json
  • convert/translate/package.json is excluded by !**/*.json
  • package-lock.json is excluded by !**/package-lock.json, !**/*.json
📒 Files selected for processing (6)
  • convert/translate/README.MD (1 hunks)
  • convert/translate/jest.config.js (1 hunks)
  • convert/translate/rollup.config.mjs (1 hunks)
  • convert/translate/src/convert.translate.plugin.spec.ts (1 hunks)
  • convert/translate/src/convert.translate.plugin.ts (1 hunks)
  • convert/translate/src/index.ts (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • convert/translate/jest.config.js
  • convert/translate/rollup.config.mjs
  • convert/translate/src/index.ts
🧰 Additional context used
🪛 LanguageTool
convert/translate/README.MD

[uncategorized] ~46-~46: Loose punctuation mark.
Context: ...ollowing properties: - sourceLanguage: The source language code (e.g., 'en' fo...

(UNLIKELY_OPENING_PUNCTUATION)

🔇 Additional comments (3)
convert/translate/src/convert.translate.plugin.spec.ts (2)

14-28: Well-structured test suite setup!

The test suite setup is well-organized and follows best practices. Using beforeEach to initialize the FlatfileListener and TranslationConfig objects ensures a clean state for each test case.


1-47: Overall, well-structured and comprehensive test suite.

The test file for convertTranslatePlugin is well-organized and covers important aspects of the plugin's functionality. It follows good testing practices such as mocking external dependencies and using beforeEach for setup.

To further improve the test suite:

  1. Consider using a more specific mock response for the Google Translate API.
  2. Add a test case that verifies the actual translation functionality of the plugin.

These enhancements will provide more comprehensive coverage and increase confidence in the plugin's behavior.

convert/translate/README.MD (1)

1-76: Documentation is comprehensive and informative

The README provides clear and detailed information about the plugin's features, installation process, usage examples, configuration options, behavior, and Google Cloud setup. The instructions are well-structured and user-friendly.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~46-~46: Loose punctuation mark.
Context: ...ollowing properties: - sourceLanguage: The source language code (e.g., 'en' fo...

(UNLIKELY_OPENING_PUNCTUATION)

Comment on lines +79 to +77
const { record: updatedRecord, error } = translateRecord(record, config)

if (error) {
updatedRecord.addError('general', error)
}

return updatedRecord
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Await the asynchronous 'translateRecord' function

With translateRecord now being asynchronous, you need to await its result in the caller to ensure proper execution flow.

Apply this diff to fix the call:

            const { record: updatedRecord, error } = 
-             translateRecord(record, config)
+             await translateRecord(record, config)

            if (error) {
              updatedRecord.addError('general', error)
            }

            return updatedRecord
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { record: updatedRecord, error } = translateRecord(record, config)
if (error) {
updatedRecord.addError('general', error)
}
return updatedRecord
const { record: updatedRecord, error } = await translateRecord(record, config)
if (error) {
updatedRecord.addError('general', error)
}
return updatedRecord

Comment on lines +19 to +21
export function translateRecord(
record: FlatfileRecord,
config: TranslationConfig
): { record: FlatfileRecord; error?: string } {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Update 'translateRecord' to be an asynchronous function

Since we're now using await inside translateRecord, the function must be declared as async, and its return type should be updated to a Promise.

Apply this diff to update the function signature:

-export function translateRecord(
+export async function translateRecord(
   record: FlatfileRecord,
   config: TranslationConfig
-): { record: FlatfileRecord; error?: string } {
+): Promise<{ record: FlatfileRecord; error?: string }> {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function translateRecord(
record: FlatfileRecord,
config: TranslationConfig
): { record: FlatfileRecord; error?: string } {
export async function translateRecord(
record: FlatfileRecord,
config: TranslationConfig
): Promise<{ record: FlatfileRecord; error?: string }> {

Comment on lines +102 to +97
} catch (error) {
console.error('Translation error:', error)
throw new Error('Failed to translate texts')
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Include original error details when throwing a new error

When catching an error and throwing a new one, it's helpful to include the original error message to aid in debugging.

Modify the code to include the original error message:

     console.error('Translation error:', error)
-    throw new Error('Failed to translate texts')
+    throw new Error(`Failed to translate texts: ${error.message}`)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (error) {
console.error('Translation error:', error)
throw new Error('Failed to translate texts')
}
} catch (error) {
console.error('Translation error:', error)
throw new Error(`Failed to translate texts: ${error.message}`)
}

Comment on lines 36 to 46
if (config.autoTranslate) {
const translatedTexts = batchTranslateText(
textsToTranslate.map((item) => item.text),
config.sourceLanguage,
config.targetLanguage
)

textsToTranslate.forEach((item, index) => {
record.set(item.targetField, translatedTexts[index])
})
} else {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix missing 'await' when calling the asynchronous function 'batchTranslateText'

The function batchTranslateText is asynchronous and returns a Promise. In translateRecord, it's called without await, which means translatedTexts will be a Promise rather than the expected array of strings. This will lead to incorrect behavior when accessing translatedTexts[index].

Apply this diff to fix the issue:

 export function translateRecord(
   record: FlatfileRecord,
   config: TranslationConfig
-): { record: FlatfileRecord; error?: string } {
+): Promise<{ record: FlatfileRecord; error?: string }> {
   try {
     const textsToTranslate = config.fieldsToTranslate
       .map((field) => ({
         text: record.get(field) as string,
         sourceField: field,
         targetField: `${field}_${config.targetLanguage}`,
       }))
       .filter((item) => item.text)

     if (textsToTranslate.length === 0) {
       return { record, error: 'No text fields to translate' }
     }

     if (config.autoTranslate) {
-      const translatedTexts = batchTranslateText(
+      const translatedTexts = await batchTranslateText(
         textsToTranslate.map((item) => item.text),
         config.sourceLanguage,
         config.targetLanguage
       )

       textsToTranslate.forEach((item, index) => {
         record.set(item.targetField, translatedTexts[index])
       })
     } else {
       // If not auto-translating, create empty fields for manual input
       textsToTranslate.forEach((item) => {
         record.set(item.targetField, '')
       })
     }

     return { record }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (config.autoTranslate) {
const translatedTexts = batchTranslateText(
textsToTranslate.map((item) => item.text),
config.sourceLanguage,
config.targetLanguage
)
textsToTranslate.forEach((item, index) => {
record.set(item.targetField, translatedTexts[index])
})
} else {
if (config.autoTranslate) {
const translatedTexts = await batchTranslateText(
textsToTranslate.map((item) => item.text),
config.sourceLanguage,
config.targetLanguage
)
textsToTranslate.forEach((item, index) => {
record.set(item.targetField, translatedTexts[index])
})
} else {

let translate: Translate

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid using a module-level variable for the Translate client

Using a module-level variable translate can lead to issues if multiple instances of the plugin are initialized with different configurations. Consider initializing the client within the functions or passing it as a parameter to ensure proper encapsulation.

Refactor the code to remove the module-level variable:

-// Initialize the Google Translate client
-let translate: Translate

...

 export function convertTranslatePlugin(
   listener: FlatfileListener,
   config: TranslationConfig
 ) {
-  // Initialize the Google Translate client with the provided configuration
-  translate = new Translate({
+  const translateClient = new Translate({
     projectId: config.projectId,
     keyFilename: config.keyFilename,
   })

...

-        const { record: updatedRecord, error } = await translateRecord(record, config)
+        const { record: updatedRecord, error } = await translateRecord(record, config, translateClient)

...

 export async function translateRecord(
   record: FlatfileRecord,
   config: TranslationConfig
+  translateClient: Translate
 ): Promise<{ record: FlatfileRecord; error?: string }> {

...

-      const translatedTexts = await batchTranslateText(
+      const translatedTexts = await batchTranslateText(
+        translateClient,
         textsToTranslate.map((item) => item.text),
         config.sourceLanguage,
         config.targetLanguage
       )

...

 async function batchTranslateText(
+  translateClient: Translate,
   texts: string[],
   sourceLanguage: string,
   targetLanguage: string
 ): Promise<string[]> {
   try {
-    const [translations] = await translate.translate(texts, {
+    const [translations] = await translateClient.translate(texts, {
       from: sourceLanguage,
       to: targetLanguage,
     })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let translate: Translate
// The module-level variable is removed
export function convertTranslatePlugin(
listener: FlatfileListener,
config: TranslationConfig
) {
const translateClient = new Translate({
projectId: config.projectId,
keyFilename: config.keyFilename,
})
// ... (other code remains unchanged)
const { record: updatedRecord, error } = await translateRecord(record, config, translateClient)
// ... (other code remains unchanged)
}
export async function translateRecord(
record: FlatfileRecord,
config: TranslationConfig,
translateClient: Translate
): Promise<{ record: FlatfileRecord; error?: string }> {
// ... (other code remains unchanged)
const translatedTexts = await batchTranslateText(
translateClient,
textsToTranslate.map((item) => item.text),
config.sourceLanguage,
config.targetLanguage
)
// ... (other code remains unchanged)
}
async function batchTranslateText(
translateClient: Translate,
texts: string[],
sourceLanguage: string,
targetLanguage: string
): Promise<string[]> {
try {
const [translations] = await translateClient.translate(texts, {
from: sourceLanguage,
to: targetLanguage,
})
// ... (other code remains unchanged)
} catch (error) {
// ... (error handling remains unchanged)
}
}

Comment on lines 46 to 60
- `sourceLanguage`: The source language code (e.g., 'en' for English)
- `targetLanguage`: The target language code (e.g., 'es' for Spanish)
- `sheetSlug`: The slug of the sheet where translation should be applied
- `fieldsToTranslate`: An array of field names to be translated
- `projectId`: Your Google Cloud project ID
- `keyFilename`: The path to your Google Cloud service account key file

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add 'autoTranslate' option to the configuration documentation

The behavior section mentions both automatic and manual translation workflows controlled by the auto-translation setting. However, the configuration options do not include an autoTranslate parameter. Consider adding autoTranslate to the configuration object to provide users with control over this feature.

Apply this diff to include the missing configuration option:

 - `fieldsToTranslate`: An array of field names to be translated
 - `projectId`: Your Google Cloud project ID
 - `keyFilename`: The path to your Google Cloud service account key file
+ - `autoTranslate`: A boolean flag to enable or disable automatic translation (default: true)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- `sourceLanguage`: The source language code (e.g., 'en' for English)
- `targetLanguage`: The target language code (e.g., 'es' for Spanish)
- `sheetSlug`: The slug of the sheet where translation should be applied
- `fieldsToTranslate`: An array of field names to be translated
- `projectId`: Your Google Cloud project ID
- `keyFilename`: The path to your Google Cloud service account key file
- `sourceLanguage`: The source language code (e.g., 'en' for English)
- `targetLanguage`: The target language code (e.g., 'es' for Spanish)
- `sheetSlug`: The slug of the sheet where translation should be applied
- `fieldsToTranslate`: An array of field names to be translated
- `projectId`: Your Google Cloud project ID
- `keyFilename`: The path to your Google Cloud service account key file
- `autoTranslate`: A boolean flag to enable or disable automatic translation (default: true)
🧰 Tools
🪛 LanguageTool

[uncategorized] ~46-~46: Loose punctuation mark.
Context: ...ollowing properties: - sourceLanguage: The source language code (e.g., 'en' fo...

(UNLIKELY_OPENING_PUNCTUATION)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 2cd67c4 and 25dd516.

📒 Files selected for processing (1)
  • convert/translate/src/convert.translate.plugin.ts (1 hunks)
🧰 Additional context used
🔇 Additional comments (6)
convert/translate/src/convert.translate.plugin.ts (6)

1-13: LGTM: Imports and interface declaration are well-structured

The imports are appropriate for the plugin's functionality, and the TranslationConfig interface is well-defined with all necessary fields for configuration.


100-100: LGTM: Appropriate default export

The default export of convertTranslatePlugin is correct and allows for easy importing of the plugin in other modules.


15-16: 🛠️ Refactor suggestion

Refactor: Avoid using a module-level variable for the Translate client

As mentioned in a previous review, using a module-level variable translate can lead to issues if multiple instances of the plugin are initialized with different configurations. Consider initializing the client within the functions or passing it as a parameter to ensure proper encapsulation.

Please refer to the previous review comment for a detailed refactoring suggestion.


18-50: ⚠️ Potential issue

Critical: Update translateRecord to be an asynchronous function and await batchTranslateText

Two critical issues need to be addressed in this function:

  1. The function should be declared as async since it calls an asynchronous function batchTranslateText.
  2. The call to batchTranslateText should be awaited.

These issues were pointed out in previous reviews and are still valid. They will cause incorrect behavior as translatedTexts will be a Promise rather than the expected array of strings.

Please refer to the previous review comments for detailed suggestions on how to fix these issues.


52-81: ⚠️ Potential issue

Update: Refactor Translate client initialization and await translateRecord

Two important changes are needed in this function:

  1. Refactor the initialization of the Translate client to avoid using a global variable, as suggested in previous reviews.
  2. Once translateRecord is made asynchronous, ensure to await its call here.

Please refer to the previous review comments for detailed suggestions on how to implement these changes.


83-98: 🛠️ Refactor suggestion

Improve: Refactor Translate client usage and enhance error handling

Two improvements can be made to this function:

  1. Refactor to receive the Translate client as a parameter instead of using the global variable, as suggested in previous reviews.
  2. Enhance the error handling by including the original error message when throwing a new error.

Please refer to the previous review comments for detailed suggestions on how to implement these improvements.

@bangarang bangarang force-pushed the feat/FieldTranslator branch from 6c4724b to c0ecfb6 Compare October 22, 2024 15:06
@bangarang bangarang enabled auto-merge (squash) October 22, 2024 15:15
@bangarang bangarang force-pushed the feat/FieldTranslator branch from 6caa688 to 982bcda Compare October 22, 2024 15:42
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 6caa688 and 982bcda.

⛔ Files ignored due to path filters (2)
  • convert/translate/package.json is excluded by !**/*.json
  • package-lock.json is excluded by !**/package-lock.json, !**/*.json
📒 Files selected for processing (7)
  • .changeset/lovely-suits-begin.md (1 hunks)
  • convert/translate/README.MD (1 hunks)
  • convert/translate/jest.config.js (1 hunks)
  • convert/translate/rollup.config.mjs (1 hunks)
  • convert/translate/src/convert.translate.plugin.spec.ts (1 hunks)
  • convert/translate/src/convert.translate.plugin.ts (1 hunks)
  • convert/translate/src/index.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • .changeset/lovely-suits-begin.md
  • convert/translate/README.MD
  • convert/translate/jest.config.js
  • convert/translate/rollup.config.mjs
  • convert/translate/src/convert.translate.plugin.spec.ts
  • convert/translate/src/index.ts
🧰 Additional context used
🔇 Additional comments (5)
convert/translate/src/convert.translate.plugin.ts (5)

1-13: LGTM: Imports and TranslationConfig interface are well-defined.

The imports cover all necessary dependencies, and the TranslationConfig interface is comprehensive, including all required fields for the plugin's functionality.


15-16: 🛠️ Refactor suggestion

Refactor: Avoid using a module-level variable for the Translate client

Using a module-level variable translate can lead to issues if multiple instances of the plugin are initialized with different configurations. Consider initializing the client within the functions or passing it as a parameter to ensure proper encapsulation.


18-50: ⚠️ Potential issue

Update: Make translateRecord an asynchronous function

The translateRecord function should be asynchronous to properly handle the asynchronous batchTranslateText call. Apply the following changes:

  1. Make the function asynchronous.
  2. Update the return type to include Promise.
  3. Await the call to batchTranslateText.

Apply this diff to update the function:

-export function translateRecord(
+export async function translateRecord(
   record: FlatfileRecord,
   config: TranslationConfig
-): { record: FlatfileRecord; error?: string } {
+): Promise<{ record: FlatfileRecord; error?: string }> {
   try {
     // ... existing code ...

-    const translatedTexts = batchTranslateText(
+    const translatedTexts = await batchTranslateText(
       textsToTranslate.map((item) => item.text),
       config.sourceLanguage,
       config.targetLanguage
     )

     // ... rest of the function ...
   }
 }

52-81: ⚠️ Potential issue

Update: Await the call to translateRecord

The convertTranslatePlugin function needs to await the call to translateRecord once it's made asynchronous. Apply the following change:

Apply this diff to update the function:

   listener.use(
     recordHook(
       config.sheetSlug,
       async (record: FlatfileRecord, event: FlatfileEvent) => {
         // ... existing code ...

-        const { record: updatedRecord, error } = translateRecord(record, config)
+        const { record: updatedRecord, error } = await translateRecord(record, config)

         // ... rest of the function ...
       }
     )
   )

83-98: 🛠️ Refactor suggestion

Improve: Include original error details in batchTranslateText

When catching an error in batchTranslateText, it's helpful to include the original error message to aid in debugging.

Apply this diff to improve error handling:

   } catch (error) {
     console.error('Translation error:', error)
-    throw new Error('Failed to translate texts')
+    throw new Error(`Failed to translate texts: ${error.message}`)
   }

Comment on lines +1 to +100
import { recordHook, FlatfileRecord } from '@flatfile/plugin-record-hook'
import { FlatfileListener, FlatfileEvent } from '@flatfile/listener'
import { Translate } from '@google-cloud/translate/build/src/v2'

// Configuration options
export interface TranslationConfig {
sourceLanguage: string
targetLanguage: string
sheetSlug: string
fieldsToTranslate: string[]
projectId: string
keyFilename: string
}

// Initialize the Google Translate client
let translate: Translate

export function translateRecord(
record: FlatfileRecord,
config: TranslationConfig
): { record: FlatfileRecord; error?: string } {
try {
const textsToTranslate = config.fieldsToTranslate
.map((field) => ({
text: record.get(field) as string,
sourceField: field,
targetField: `${field}_${config.targetLanguage}`,
}))
.filter((item) => item.text)

if (textsToTranslate.length === 0) {
return { record, error: 'No text fields to translate' }
}

const translatedTexts = batchTranslateText(
textsToTranslate.map((item) => item.text),
config.sourceLanguage,
config.targetLanguage
)

textsToTranslate.forEach((item, index) => {
record.set(item.targetField, translatedTexts[index])
})

return { record }
} catch (error) {
console.error('Error processing record:', error)
return { record, error: 'An error occurred while processing this record' }
}
}

export function convertTranslatePlugin(
listener: FlatfileListener,
config: TranslationConfig
) {
// Initialize the Google Translate client with the provided configuration
translate = new Translate({
projectId: config.projectId,
keyFilename: config.keyFilename,
})

listener.use(
recordHook(
config.sheetSlug,
async (record: FlatfileRecord, event: FlatfileEvent) => {
// Check if this record is from the configured sheet
if (event.context.sheetSlug !== config.sheetSlug) {
return record
}

const { record: updatedRecord, error } = translateRecord(record, config)

if (error) {
updatedRecord.addError('general', error)
}

return updatedRecord
}
)
)
}

async function batchTranslateText(
texts: string[],
sourceLanguage: string,
targetLanguage: string
): Promise<string[]> {
try {
const [translations] = await translate.translate(texts, {
from: sourceLanguage,
to: targetLanguage,
})
return Array.isArray(translations) ? translations : [translations]
} catch (error) {
console.error('Translation error:', error)
throw new Error('Failed to translate texts')
}
}

export default convertTranslatePlugin
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Critical Issues Remain Unresolved in Plugin Implementation

Several critical issues identified in previous reviews remain unaddressed:

  1. The translateRecord function is not asynchronous and does not await the call to batchTranslateText.
  2. In convertTranslatePlugin, the call to translateRecord is not awaited.
  3. Error handling in batchTranslateText does not include the original error message for better debugging.

Please prioritize addressing these concerns in your next iteration to ensure the plugin operates correctly and handles translations robustly.

🔗 Analysis chain

Critical Issues Remain Unresolved in Plugin Implementation

Several critical issues identified in previous reviews remain unaddressed:

  1. The use of a module-level translate variable should be refactored to avoid potential conflicts with multiple plugin instances.
  2. The translateRecord function needs to be made asynchronous and properly await the call to batchTranslateText.
  3. In convertTranslatePlugin, the call to translateRecord should be awaited once it's made asynchronous.
  4. Error handling in batchTranslateText should include the original error message for better debugging.

Please prioritize addressing these concerns in your next iteration to ensure the plugin operates correctly and handles translations robustly.

To verify these changes have been implemented, you can run the following script:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify the changes in the plugin implementation

# Check if translateRecord is async and awaits batchTranslateText
echo "Checking translateRecord implementation:"
ast-grep --lang typescript --pattern $'export async function translateRecord(
  $_,
  $_
): Promise<$_> {
  $$$
  const translatedTexts = await batchTranslateText(
    $$$
  )
  $$$
}' convert/translate/src/convert.translate.plugin.ts

# Check if convertTranslatePlugin awaits translateRecord
echo "Checking convertTranslatePlugin implementation:"
ast-grep --lang typescript --pattern $'export function convertTranslatePlugin($_) {
  $$$
  const { record: updatedRecord, error } = await translateRecord($_)
  $$$
}' convert/translate/src/convert.translate.plugin.ts

# Check if batchTranslateText includes original error message
echo "Checking batchTranslateText error handling:"
ast-grep --lang typescript --pattern $'async function batchTranslateText($_) {
  $$$
  throw new Error(`Failed to translate texts: ${error.message}`)
  $$$
}' convert/translate/src/convert.translate.plugin.ts

Length of output: 957

@bangarang bangarang merged commit 59a9db0 into main Oct 22, 2024
@bangarang bangarang deleted the feat/FieldTranslator branch October 22, 2024 15:56
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.

2 participants