Skip to content

Commit

Permalink
Merge pull request #4 from tzdesign/feat/compiled-i18n
Browse files Browse the repository at this point in the history
Feat/compiled i18n
  • Loading branch information
tzdesign authored Nov 30, 2023
2 parents 5cef0e5 + 667eb4d commit 341bd5e
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 23 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
node_modules
src/test/*
!src/test/en-US.json
!src/test/message-en.json
!src/test/message-en.json
!src/test/compiled-i18n/en-US.json
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
"test:translate:withprefix": "tsc && node dist/index.js translate -b src/test/message-en.json -l de-DE de-CH fr-FR sk-SK -i de-DE",
"test:stale": "tsc && node dist/index.js remove-stale -b src/test/en-US.json -l de-DE de-CH sk-SK fr-FR",
"test:stale:withprefix": "tsc && node dist/index.js remove-stale -b src/test/message-en.json -l de-DE de-CH sk-SK fr-FR",
"test:drystale": "tsc && node dist/index.js remove-stale -d -b src/test/en-US.json -l de-DE de-CH sk-SK fr-FR"
"test:drystale": "tsc && node dist/index.js remove-stale -d -b src/test/en-US.json -l de-DE de-CH sk-SK fr-FR",
"test:stale:compiled-i18n": "tsc && node dist/index.js remove-stale -b src/test/compiled-i18n/en-US.json -l de-DE fr-FR sk-SK --compiled-i18n",
"test:compiled-i18n": "tsc && node dist/index.js translate -b src/test/compiled-i18n/en-US.json -l de-DE fr-FR sk-SK -i de-DE --compiled-i18n"
},
"keywords": [
"localization",
Expand Down
67 changes: 47 additions & 20 deletions src/translate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,14 @@ export default async function translate(this: Command) {
);

const filteredTranslations = Object.entries(translations).filter(
([key]) => targetTranslation.translations[key] === undefined
([key]) => {
const translation = targetTranslation.translations[key];

return (
translation === undefined ||
(typeof translation === "string" && translation.trim() === "")
);
}
);

if (filteredTranslations.length === 0) {
Expand All @@ -121,7 +128,10 @@ export default async function translate(this: Command) {
}

progress.start(filteredTranslations.length, 0);
for (const [key, sourceText] of filteredTranslations) {
for (let [key, sourceText] of filteredTranslations) {
if (options.compiledI18n && Boolean(sourceText) === false) {
sourceText = key;
}
progress.increment();
const metaKey =
targetLocale.language +
Expand All @@ -130,27 +140,44 @@ export default async function translate(this: Command) {
: "");

if (targetTranslation.translations[key]) continue;
if (meta[metaKey]?.[key]) {
targetTranslation.translations[key] = meta[metaKey][key];
if (meta[metaKey]?.[JSON.stringify(sourceText)]) {
targetTranslation.translations[key] =
meta[metaKey][JSON.stringify(sourceText)];
continue;
}
const result = await translator.translateText(
prepareVariables(sourceText),
sourceLanguage.code as deepl.SourceLanguageCode,
targetLanguage.code as deepl.TargetLanguageCode,
{
formality:
options.informalLocales?.includes(targetLocaleString) &&
targetLanguage.supportsFormality
? "less"
: undefined,
tagHandling: "xml",
ignoreTags: ["x"],
}
);
targetTranslation.translations[key] = cleanVariables(result.text);

meta[metaKey] = meta[metaKey] ?? {};
meta[metaKey][key] = cleanVariables(result.text);

const object =
typeof sourceText === "string" ? { flat: sourceText } : sourceText;

for (const [plural, source] of Object.entries(object)) {
const result = await translator.translateText(
prepareVariables(source),
sourceLanguage.code as deepl.SourceLanguageCode,
targetLanguage.code as deepl.TargetLanguageCode,
{
formality:
options.informalLocales?.includes(targetLocaleString) &&
targetLanguage.supportsFormality
? "less"
: undefined,
tagHandling: "xml",
ignoreTags: ["x"],
}
);
if (plural === "flat") {
targetTranslation.translations[key] = cleanVariables(result.text);
} else {
targetTranslation.translations[key] =
targetTranslation.translations[key] ?? {};
(targetTranslation.translations[key] as Record<string, string>)[
plural
] = cleanVariables(result.text);
}

meta[metaKey][source] = cleanVariables(result.text);
}
}
progress.stop();
console.log("");
Expand Down
6 changes: 6 additions & 0 deletions src/translate/translationOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type TranslationOptions = {
locales: string[];
base?: string;
informalLocales: string[];
compiledI18n: boolean;
};

export function appendTranslationOption(command: Command) {
Expand All @@ -19,5 +20,10 @@ export function appendTranslationOption(command: Command) {
"-l, --locales [value...]",
"Locales to translate to. For example de-DE,fr-FR. If none is given, no translation will happen.",
[]
)
.option(
"-c, --compiled-i18n",
"Automatically translate compiled-i18n files. They are a bit different than $localize files, as the base file translation might be empty.",
false
);
}
7 changes: 6 additions & 1 deletion src/translate/validateBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ export function validateBase(string: string) {
}
}

export const localizeTranslationSchema = z.union([
z.string(),
z.record(z.string()),
]);

export const localizeFileSchema = z.object({
locale: z.string(),
translations: z.record(z.string()),
translations: z.record(localizeTranslationSchema),
});

export type LocalizeFile = z.infer<typeof localizeFileSchema>;

0 comments on commit 341bd5e

Please sign in to comment.