Skip to content

Commit

Permalink
Merge pull request #595 from ahsoj22/main
Browse files Browse the repository at this point in the history
Fix to account for name conflicts during project translation
  • Loading branch information
amyjko authored Nov 21, 2024
2 parents 967d96a + ef0e8c4 commit 3469477
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 6 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,4 @@
"defaults",
"not op_mini all"
]
}
}
101 changes: 101 additions & 0 deletions src/models/translate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { vi } from 'vitest';
import type { Functions } from 'firebase/functions';
import type Project from './Project';

// Create a mock translation function that we'll pass to the actual implementation
const createMockTranslateFunction = (mockResponse: string[]) => {
return async (functions: Functions, project: Project, targetLanguage: string) => {
const primaryLang = project.getPrimaryLanguage();
const sources = project.getSources();

// Process the sources and handle name conflicts
const existingNames = sources[0].nodes().map(node =>
node.getNameInLanguage()?.getName()
);

// Simulate translation logic
const translatedNames = mockResponse;

// Handle name conflicts
const getUniqueNames = (baseName: string): string => {
let counter = 1;
let newName = baseName;
while (existingNames.includes(newName)) {
counter++;
newName = `${baseName}${counter}`;
}
return newName;
};

// Create revised nodes with translated names
const revisedNodes = sources[0].nodes().map((node, index) => {
const translatedName = getUniqueNames(translatedNames[index]);
return [
node,
{
names: [{
getName: () => translatedName,
isLanguage: () => true,
hasLanguage: () => false
}]
}
];
});

// Update project with new translations
project.withPrimaryLocale();
project.withRevisedNodes(revisedNodes);

return project;
};
};

describe('translateProject', () => {
let mockFunctions: Functions;
let mockProject: Project;
let translateProject: any;

beforeEach(() => {
mockFunctions = {} as Functions;

// Create mock project
mockProject = {
getPrimaryLanguage: vi.fn().mockReturnValue('en'),
getSources: vi.fn(),
withPrimaryLocale: vi.fn(),
withRevisedNodes: vi.fn(),
getContext: vi.fn(),
getRoot: vi.fn(),
} as unknown as Project;

// Mock source nodes
const mockNode = {
instanceof: vi.fn().mockReturnValue(true),
getNameInLanguage: vi.fn().mockReturnValue({ getName: () => 'existingName' }),
names: [{ getName: () => 'existingName', isLanguage: () => true, hasLanguage: () => false }],
withName: vi.fn().mockReturnValue({}),
};

(mockProject.getSources as ReturnType<typeof vi.fn>).mockReturnValue([{
nodes: vi.fn().mockReturnValue([mockNode])
}]);
});

it('should handle duplicate name conflicts by appending a number', async () => {
// Create mock translation function with predetermined response
translateProject = createMockTranslateFunction(['existingName']);

const result = await translateProject(mockFunctions, mockProject, 'es-ES');

expect(result).not.toBeNull();
expect(mockProject.withPrimaryLocale).toHaveBeenCalled();
expect(mockProject.withRevisedNodes).toHaveBeenCalled();

// Verify the conflict resolution by appending a number
const revisedNodesCall = (mockProject.withRevisedNodes as ReturnType<typeof vi.fn>).mock.calls[0][0];
const revisedName = revisedNodesCall[0][1]?.names[0].getName();

expect(revisedName).toBe('existingName2');
});
});
35 changes: 31 additions & 4 deletions src/models/translate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { httpsCallable, type Functions } from 'firebase/functions';
import Reference from '@nodes/Reference';
import type Source from '@nodes/Source';
import Names from '@nodes/Names';
import { Locales } from '@db/Database';
import { Locales } from '@db/Database';
import BinaryEvaluate from '@nodes/BinaryEvaluate';
import Docs from '@nodes/Docs';
import Doc from '@nodes/Doc';
Expand Down Expand Up @@ -34,6 +34,19 @@ export default async function translateProject(
// Get the project's primary language.
const sourceLanguage = project.getPrimaryLanguage();

// Keep track of existing names in target language
const existingNames = new Set<string>();

// collect existing names in target language
project.getSources().forEach((source) => {
source.nodes()
.filter((node): node is Names => node instanceof Names)
.forEach((names) => {
const targetName = names.getNameInLanguage(targetLanguage, undefined)?.getName();
if (targetName) existingNames.add(targetName);
});
});

// Find all of the names binds in the project's sources. We're going to add translated names to them, and update references to those names, if necessary.
// Convert the binds into a record of translations to perform.
const bindsToTranslate = project
Expand Down Expand Up @@ -66,9 +79,9 @@ export default async function translateProject(
// The original text to translate, or undefined if there is no text to translate.
// Convert the camel cased name into separated words for better translation performance.
original: nameToTranslate
.getName()
?.replace(SeparateWords, ' $&')
.trim(),
.getName()
?.replace(SeparateWords, ' $&')
.trim(),
// The translation, or undefined if there is no translation yet.
translation: targetName,
};
Expand Down Expand Up @@ -190,6 +203,17 @@ export default async function translateProject(
: word.charAt(0).toUpperCase() + word.slice(1),
)
.join('');
if (existingNames.has(translation)) {
let counter = 2;
// Increment the counter until a unique name is found
while (existingNames.has(`${translation}${counter}`)) {
counter++;
}
translation = `${translation}${counter}`;
}

//Add the unique translation to the set
existingNames.add(translation);
}

// If we have a translation, add it to the bind and update its references.
Expand Down Expand Up @@ -304,3 +328,6 @@ export default async function translateProject(
return null;
}
}



0 comments on commit 3469477

Please sign in to comment.