Skip to content

Conversation

@bparrish17
Copy link

@bparrish17 bparrish17 commented Dec 18, 2025

Overview

  • Adds iosLocales to project YAML config
  • If iosLocales provided in conjunction with ios-strings or ios-stringsdict outputs, will write mapped variants to a <locale>.proj directory in the project YAML's outDir
  • Swift file will be generated that maps values to locale directories generated above
  • If variant configured in YAML but is NOT included as key in iosLocales, will write to output's outDir as usual

Context

DIT-12000: [CLI] Add iOSLocales Configuration

Test Plan

Config setup: Make sure to have iosLocales at the root of your project directory. Ideally you have a few variants in your local setup. If so, have at least a couple of them mapped to a locale, e.g. base: en, spanish: es

projects:
  - id: dittopay-v2-qa-file-bp-copy
  - id: cli-testing-project
variants:
  - id: base
  - id: spanish
  - id: japanese
components:
  folders:
    - id: root
iosLocales:
  - base: en
  - spanish: es
outputs:
  - format: ios-strings
    outDir: "./ditto/locale-testing/ios-strings"

  - format: ios-stringsdict
    outDir: "./ditto/locale-testing/ios-stringsdict"

This setup allowed me to ensure that spanish and english files were written to the project's outDir and not the individual output's outDir

  • Run yarn start pull, english and spanish variant files for both .strings and .stringsdict should be written to an en.lproj and es.lproj directory at the root of the project's outDir
  • A Ditto.swift file should be written to the root of the project's outDir. It should have actual swift code in it lol
  • Variants not configured in iosLocales should write to the output's outDir as expected

@bparrish17 bparrish17 changed the base branch from master to string-file-formats December 18, 2025 16:53
@bparrish17 bparrish17 marked this pull request as ready for review December 18, 2025 17:28
@bparrish17 bparrish17 requested a review from joustrich December 18, 2025 17:38
@bparrish17 bparrish17 requested review from JWhite30515 and removed request for joustrich December 19, 2025 14:50
@bparrish17 bparrish17 assigned JWhite30515 and unassigned joustrich Dec 19, 2025
@bparrish17 bparrish17 requested review from laurakoye and removed request for JWhite30515 December 19, 2025 19:24
@bparrish17 bparrish17 assigned laurakoye and unassigned JWhite30515 Dec 19, 2025
Copy link

@laurakoye laurakoye left a comment

Choose a reason for hiding this comment

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

I didn't encounter any issues testing. I know you wanted to get another review, so I'll just leave my comments.

const swiftDriverFile = await this.getSwiftDriverFile();
files.push(swiftDriverFile);
}
await super.writeFiles([...files]);

Choose a reason for hiding this comment

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

You don't need the spread here

Comment on lines +14 to +21
} catch (e) {
if (!(e instanceof AxiosError)) {
throw new Error(
"Sorry! We're having trouble reaching the Ditto API. Please try again later."
);
}

throw e;

Choose a reason for hiding this comment

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

nit: You could add some more specific errors based on the http status of the response below?

describe("getLocalesPath", () => {
it("should return output outDir when iosLocales is not configured", () => {
const projectConfig = createMockProjectConfig({
iosLocales: undefined,

Choose a reason for hiding this comment

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

Add test for iosLocales: []

// map "base" to undefined, as by default export endpoint returns base variant
const variantsParam =
variant.id === "base" ? undefined : [{ id: variant.id }];
const variantId = variant.id === "base" ? undefined : variant.id;

Choose a reason for hiding this comment

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

In general we could extract some commonly used constants into a constants file?

export const BASE_VARIANT_ID = "base";
export const SWIFT_DRIVER_FILENAME = "Ditto";
export const FILE_NAME_SEPARATOR = "___";
export const COMPONENTS_FILE_PREFIX = "components";

Comment on lines +462 to +464
/***********************************************************
* getLocalesPath
***********************************************************/

Choose a reason for hiding this comment

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

I feel like this comment is unnecessary if we have the describe block

@@ -0,0 +1,16 @@
import OutputFile from "./OutputFile";

export default class SwiftOutputFile extends OutputFile<string, {}> {

Choose a reason for hiding this comment

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

Do we want the file name to match the class name?


protected async writeFiles(files: OutputFile[]): Promise<void> {
if (this.projectConfig.iosLocales) {
const swiftDriverFile = await this.getSwiftDriverFile();

Choose a reason for hiding this comment

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

If we call writeFiles for both iosStringsDict and iosStrings. Does that mean we are generating the driver file twice?

excludeNestedFolders: z.boolean().optional(),
})).optional(),
}).optional(),
components: z

Choose a reason for hiding this comment

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

Is this just a format diff? I don't see what changed?

"start": "node esbuild.mjs && node --enable-source-maps --require dotenv/config bin/ditto.js",
"sync": "node esbuild.mjs && node --enable-source-maps --require dotenv/config bin/ditto.js pull"
"sync": "node esbuild.mjs && node --enable-source-maps --require dotenv/config bin/ditto.js pull",
"sync-legacy": "node esbuild.mjs && node --enable-source-maps --require dotenv/config bin/ditto.js --legacy pull"

Choose a reason for hiding this comment

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

Why do we need this?

variants: z.array(z.object({ id: z.string() })).optional(),
outDir: z.string().optional(),
richText: z.union([z.literal("html"), z.literal(false)]).optional(),
iosLocales: z.array(z.record(z.string(), z.string())).optional(),

Choose a reason for hiding this comment

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

If we care about validating inputs more, we could use .refine and we check if all the variants in iosLocales are in the variants array.

Copy link

@laurakoye laurakoye left a comment

Choose a reason for hiding this comment

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

Found what I think is an issue:
https://www.loom.com/share/d619af17b17d4f7f95991f894ed2600c

If I have a variant applied to a text item the generated string and string dics are showing the base text. This is true even if the variant has plurals on it.

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.

5 participants