> ## Documentation Index
> Fetch the complete documentation index at: https://hyperlocalise.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# iOS Strings

> Localise a Swift app with Localizable.strings, Cloud review, and locale-specific lproj folders.

This tutorial connects a Swift app's `Localizable.strings` files to Hyperlocalise Cloud. The CLI preserves keys, comments, and file formatting while replacing translated values.

## Prerequisites

You need:

* an iOS project that uses `.strings` resources;
* the [Hyperlocalise CLI](/cli/getting-started/install);
* a Cloud project with `en-US` as source and `fr-FR` and `de-DE` as targets; and
* `HYPERLOCALISE_API_KEY` and `HYPERLOCALISE_PROJECT_ID` in your environment.

The example uses:

```text theme={null}
ios/App/
├── en-US.lproj/Localizable.strings
├── fr-FR.lproj/Localizable.strings
└── de-DE.lproj/Localizable.strings
```

Add these folders to the app target in Xcode so each file is included in the built bundle.

## 1. Move visible copy into `Localizable.strings`

Create `ios/App/en-US.lproj/Localizable.strings`:

```text theme={null}
/* Navigation title on the saved filters screen */
"filters.title" = "Saved filters";

/* Confirmation shown after the user saves a filter. %@ is the filter name. */
"filters.saved.confirmation" = "Saved “%@”";

/* Button that removes the selected saved filter */
"filters.delete.action" = "Delete filter";
```

Use stable semantic keys in Swift:

```swift theme={null}
let title = String(localized: "filters.title")

let confirmation = String(
    format: String(localized: "filters.saved.confirmation"),
    filterName
)
```

Descriptions beside each entry give reviewers the context that short UI strings lack. Keep placeholder meaning in the comment.

## 2. Map the locale folders

Create `i18n.yml` at the repository root:

```yaml theme={null}
locales:
  source: en-US
  targets:
    - fr-FR
    - de-DE

buckets:
  ios:
    files:
      - from: ios/App/{{source}}.lproj/Localizable.strings
        to: ios/App/{{target}}.lproj/Localizable.strings

hyperlocalise:
  project_id_env: HYPERLOCALISE_PROJECT_ID
  api_base_url: https://hyperlocalise.com/api
  api_key_env: HYPERLOCALISE_API_KEY
```

If your project uses language-only folders such as `fr.lproj`, use target locale names that match those folders or map separate buckets to the exact paths your Xcode project expects.

## 3. Check the source pull request

Before merge, verify the upload plan:

```bash theme={null}
hl sync push --dry-run
```

Also run your normal Xcode build and tests. A source pull request should confirm:

* every localized lookup has a source entry;
* duplicate keys do not exist;
* the `.strings` file uses valid quoted key/value syntax; and
* placeholders such as `%@`, `%d`, and positional variants remain explicit.

Do not copy English values into target files as placeholders. A target that equals the source can look complete while remaining untranslated.

## 4. Push source strings

After the source pull request merges:

```bash theme={null}
hl sync push
```

The command uploads `en-US.lproj/Localizable.strings`. It does not start translation. Use a **Source upload** automation or create a request in Cloud.

## 5. Review iOS strings in Cloud

In **Content Editor**, check:

* comments that explain the screen and control;
* `%@`, `%d`, or positional placeholders;
* terminology used in navigation and buttons;
* punctuation and smart quotes; and
* likely expansion in compact layouts.

Attach a screenshot when a label has limited width. Approve each locale after linguistic review.

## 6. Pull translated `.strings` files

Run:

```bash theme={null}
hl sync pull
hl check --bucket ios --quiet
```

Hyperlocalise writes:

```text theme={null}
ios/App/fr-FR.lproj/Localizable.strings
ios/App/de-DE.lproj/Localizable.strings
```

It keeps the source file's comments, keys, ordering, and key/value formatting while replacing value literals.

Open the result as a translation pull request. Review the diff and run the app in every target language.

## 7. Test in Xcode

For each target locale:

1. Edit the scheme.
2. Under **Run** → **Options**, set **App Language**.
3. Open the translated screens.
4. Exercise values that substitute `%@` and numeric placeholders.
5. Check compact devices and accessibility text sizes.

Add a test that catches a missing key:

```swift theme={null}
func testSavedFiltersTitleIsLocalized() {
    let bundle = Bundle(for: AppDelegate.self)
    let value = bundle.localizedString(
        forKey: "filters.title",
        value: nil,
        table: nil
    )

    XCTAssertNotEqual(value, "filters.title")
}
```

## Plurals with `.stringsdict`

Use `.stringsdict` when the app already depends on Apple's legacy plural format. Add a second mapping:

```yaml theme={null}
buckets:
  ios:
    files:
      - from: ios/App/{{source}}.lproj/Localizable.strings
        to: ios/App/{{target}}.lproj/Localizable.strings
      - from: ios/App/{{source}}.lproj/Localizable.stringsdict
        to: ios/App/{{target}}.lproj/Localizable.stringsdict
```

Hyperlocalise treats `.stringsdict` as an already-segmented format. Keep plural variable names and format specifiers stable, then test `0`, `1`, and several plural values in the app.

If you use modern Xcode String Catalogs instead, map `.xcstrings` files as described in [i18n configuration](/cli/configuration/i18n-config#apple-string-catalog-mapping-patterns).

## Troubleshooting

### Xcode shows the key instead of a translation

Confirm the target `.lproj` folder belongs to the app target, the filename is `Localizable.strings`, and the Swift lookup uses the exact key.

### Placeholder checks fail

Restore the source placeholder names and positions in the target value. Positional placeholders are safer when a language needs to reorder arguments.

### Pull skips a locale

Confirm that locale has translations in the selected Cloud project. Run `hl sync pull --dry-run` and compare the resolved path with the Xcode localization folder.

## Next

* [GitHub localisation workflow](/platform/tutorials/github-localisation-workflow)
* [Android string resources](/platform/tutorials/android-string-resources)
* [i18n configuration](/cli/configuration/i18n-config)
