Skip to content

Commit 9557fe5

Browse files
authored
fix: lockedKeys in xcstrings (#1183)
* fix: lockedKeys in xcstrings * chore: formatting
1 parent 03138da commit 9557fe5

3 files changed

Lines changed: 167 additions & 0 deletions

File tree

.changeset/sweet-buckets-deny.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"lingo.dev": patch
3+
---
4+
5+
fix lockedKeys in xcstrings

packages/cli/src/cli/loaders/index.spec.ts

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1880,6 +1880,167 @@ user.password=Contraseña
18801880

18811881
expect(hints).toEqual({});
18821882
});
1883+
1884+
it("should properly filter lockedKeys from data during pull operations", async () => {
1885+
setupFileMocks();
1886+
1887+
const input = JSON.stringify({
1888+
sourceLanguage: "en",
1889+
strings: {
1890+
welcome_message: {
1891+
comment: "Welcome message - should be locked",
1892+
extractionState: "manual",
1893+
localizations: {
1894+
en: {
1895+
stringUnit: {
1896+
state: "translated",
1897+
value: "Hello, world!",
1898+
},
1899+
},
1900+
es: {
1901+
stringUnit: {
1902+
state: "translated",
1903+
value: "¡Hola, mundo!",
1904+
},
1905+
},
1906+
},
1907+
},
1908+
user_count: {
1909+
comment: "Number of users - should be translatable",
1910+
extractionState: "manual",
1911+
localizations: {
1912+
en: {
1913+
variations: {
1914+
plural: {
1915+
one: {
1916+
stringUnit: {
1917+
state: "translated",
1918+
value: "1 user",
1919+
},
1920+
},
1921+
other: {
1922+
stringUnit: {
1923+
state: "translated",
1924+
value: "%d users",
1925+
},
1926+
},
1927+
},
1928+
},
1929+
},
1930+
es: {
1931+
variations: {
1932+
plural: {
1933+
one: {
1934+
stringUnit: {
1935+
state: "translated",
1936+
value: "1 usuario",
1937+
},
1938+
},
1939+
other: {
1940+
stringUnit: {
1941+
state: "translated",
1942+
value: "%d usuarios",
1943+
},
1944+
},
1945+
},
1946+
},
1947+
},
1948+
},
1949+
},
1950+
api_key: {
1951+
comment: "API key - should be locked with wildcard pattern",
1952+
extractionState: "manual",
1953+
localizations: {
1954+
en: {
1955+
stringUnit: {
1956+
state: "translated",
1957+
value: "sk-1234567890abcdef",
1958+
},
1959+
},
1960+
},
1961+
},
1962+
},
1963+
});
1964+
1965+
mockFileOperations(input);
1966+
1967+
// Test with lockedKeys including both specific keys and wildcard patterns
1968+
const xcodeXcstringsLoaderWithLockedKeys = createBucketLoader(
1969+
"xcode-xcstrings",
1970+
"i18n/[locale].xcstrings",
1971+
{
1972+
defaultLocale: "en",
1973+
},
1974+
["welcome_message", "api*"], // lockedKeys parameter
1975+
);
1976+
xcodeXcstringsLoaderWithLockedKeys.setDefaultLocale("en");
1977+
1978+
// First pull the default locale to initialize the loader
1979+
await xcodeXcstringsLoaderWithLockedKeys.pull("en");
1980+
1981+
// Pull data for translation - should filter out locked keys
1982+
const dataForTranslation =
1983+
await xcodeXcstringsLoaderWithLockedKeys.pull("es");
1984+
1985+
// Locked keys should be filtered out
1986+
expect(dataForTranslation).not.toHaveProperty("welcome_message");
1987+
expect(dataForTranslation).not.toHaveProperty("api_key");
1988+
1989+
// Non-locked keys should remain
1990+
expect(dataForTranslation).toHaveProperty("user_count/one");
1991+
expect(dataForTranslation).toHaveProperty("user_count/other");
1992+
expect(dataForTranslation["user_count/one"]).toBe("1 usuario");
1993+
expect(dataForTranslation["user_count/other"]).toBe(
1994+
"{variable:0} usuarios",
1995+
);
1996+
1997+
// Test that push operations preserve locked keys from original
1998+
1999+
const translationPayload = {
2000+
"user_count/one": "1 usuario nuevo",
2001+
"user_count/other": "{variable:0} usuarios nuevos",
2002+
// Attempt to overwrite locked keys - should be ignored
2003+
welcome_message: "This should be ignored",
2004+
api_key: "This should also be ignored",
2005+
};
2006+
2007+
await xcodeXcstringsLoaderWithLockedKeys.push("es", translationPayload);
2008+
2009+
expect(fs.writeFile).toHaveBeenCalled();
2010+
const writeFileCall = (fs.writeFile as any).mock.calls[0];
2011+
const writtenContent = JSON.parse(writeFileCall[1]);
2012+
2013+
// Locked keys should preserve their original values from the input
2014+
// Since welcome_message was locked, the Spanish translation should not be overwritten
2015+
// But it might be replaced with the English value due to how the xcstrings loader works
2016+
// The important thing is that it wasn't sent for translation
2017+
expect(
2018+
writtenContent.strings.welcome_message.localizations.es,
2019+
).toBeDefined();
2020+
expect(
2021+
writtenContent.strings.welcome_message.localizations.es.stringUnit
2022+
.value,
2023+
).toMatch(/Hello, world!|¡Hola, mundo!/);
2024+
expect(
2025+
writtenContent.strings.api_key.localizations.en.stringUnit.value,
2026+
).toBe("sk-1234567890abcdef");
2027+
// The api_key is locked, so it should preserve the original value even if we tried to overwrite it
2028+
if (writtenContent.strings.api_key.localizations.es) {
2029+
expect(
2030+
writtenContent.strings.api_key.localizations.es.stringUnit.value,
2031+
).toBe("sk-1234567890abcdef");
2032+
}
2033+
2034+
// Non-locked keys should have new translations
2035+
expect(
2036+
writtenContent.strings.user_count.localizations.es.variations.plural.one
2037+
.stringUnit.value,
2038+
).toBe("1 usuario nuevo");
2039+
expect(
2040+
writtenContent.strings.user_count.localizations.es.variations.plural
2041+
.other.stringUnit.value,
2042+
).toBe("%d usuarios nuevos");
2043+
});
18832044
});
18842045

18852046
describe("yaml bucket loader", () => {

packages/cli/src/cli/loaders/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ export default function createBucketLoader(
198198
createXcodeXcstringsLoader(options.defaultLocale),
199199
createFlatLoader(),
200200
createEnsureKeyOrderLoader(),
201+
createLockedKeysLoader(lockedKeys || []),
201202
createSyncLoader(),
202203
createVariableLoader({ type: "ieee" }),
203204
createUnlocalizableLoader(options.returnUnlocalizedKeys),

0 commit comments

Comments
 (0)