localship 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -12
- package/dist/{chunk-QS3UG3KI.js → chunk-GNL5EFNP.js} +468 -248
- package/dist/cli.js +71 -45
- package/dist/{server-T6GQ6CV5.js → server-5AITZXZV.js} +27 -17
- package/package.json +2 -1
- package/skills/localship/SKILL.md +66 -43
- package/skills/localship-extract/SKILL.md +123 -39
- package/skills/localship-extract/references/platform-localization.md +65 -0
- package/skills/localship-review/SKILL.md +9 -1
- package/skills/localship-setup/SKILL.md +50 -36
- package/skills/localship-translate/SKILL.md +26 -18
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: localship-extract
|
|
3
|
-
description: Make an Android or iOS app localizable by moving text hardcoded in Kotlin, XML layouts or Swift into string resources or a String Catalog, without changing behavior. Use when
|
|
3
|
+
description: Make an Android or iOS app localizable by moving text hardcoded in Kotlin, XML layouts or Swift into string resources or a String Catalog, without changing behavior. Use when the user wants text ready for translation or hardcoded strings found, however they phrase it, and as the first step when they want the app in another language but its text is still in the code.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Make an app localizable
|
|
@@ -14,9 +14,8 @@ the rules below keep it safe. Change how text is looked up, never what the app d
|
|
|
14
14
|
`localship detect` (or `localship_detect`) gives a first estimate: how much UI text is still in the
|
|
15
15
|
code, per module, with examples. Use it to size the work, then search properly as below.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
feature at a time and build after each.
|
|
17
|
+
Do the scope the user wants: the whole app unless they named particular screens or features. Search that code for user-facing literals, say in one line how
|
|
18
|
+
much you found, and start. Build as you go, and give a short progress update after each part.
|
|
20
19
|
|
|
21
20
|
**Android (Kotlin, Compose, Views)**, look for string literals in:
|
|
22
21
|
- `Text("…")`, `text = "…"`, `label = { Text("…") }`, `title = "…"`, `placeholder`, `contentDescription = "…"`
|
|
@@ -32,59 +31,136 @@ feature at a time and build after each.
|
|
|
32
31
|
`alert`/`confirmationDialog` text passed as `String`, `Button(title)` with a `String`
|
|
33
32
|
- UIKit: `label.text = "…"`, `button.setTitle("…", for:)`, `title = "…"`
|
|
34
33
|
- notification content, widget text, App Intents titles
|
|
34
|
+
- default UI text in parameters (`title: String = "Cancel"`): not extracted until the parameter
|
|
35
|
+
is a `LocalizedStringResource` or the default is wrapped
|
|
36
|
+
|
|
37
|
+
**Sentences built from English grammar** (both platforms). Wrapping these in a resource does
|
|
38
|
+
nothing; search for them and rewrite each sentence whole:
|
|
39
|
+
- pieces joined around a value (`"Finished " + amount + " over your plan."`, often so the value
|
|
40
|
+
can be styled): one complete sentence per case with placeholders, styling the value inside it
|
|
41
|
+
(`AttributedString(localized:)`; Android annotations or spans on the placeholder)
|
|
42
|
+
- plural endings in code (`count == 1 ? "" : "s"`, `== 1 ?` near text): plural resources
|
|
43
|
+
- helpers that spell out numbers, write ordinals or choose "a"/"an" (names with `spell`,
|
|
44
|
+
`ordinal`, `article`): formatters (`.formatted(.number.notation(.spellOut))`, an ordinal
|
|
45
|
+
`NumberFormatter`; ICU `RuleBasedNumberFormat` and `MessageFormat` on Android) or one sentence
|
|
46
|
+
per case
|
|
47
|
+
- text surgery on UI strings (`replacingOccurrences`, `replace`, `removePrefix`): store both forms
|
|
48
|
+
- lists (`joined(separator: ", ")` plus "and"): `.formatted(.list(type: .and))`,
|
|
49
|
+
`ListFormatter`, or ICU `ListFormatter` on Android
|
|
50
|
+
|
|
51
|
+
Rewrite even when it adds variants, and keep the English output identical so tests still pass.
|
|
35
52
|
|
|
36
53
|
**Leave alone:** log messages, analytics event names, preference and database keys, routes and
|
|
37
54
|
deep links, test tags and accessibility identifiers, URLs, format patterns, debug-only screens,
|
|
38
|
-
and anything never shown to users.
|
|
39
|
-
|
|
55
|
+
and anything never shown to users. Also text that doubles as data: enum raw values, `case`
|
|
56
|
+
patterns, query item names, dictionary keys, and any string that's parsed back, compared or saved
|
|
57
|
+
(picker values parsed into intervals, values written to export files). To show those translated,
|
|
58
|
+
add a separate display property and keep the stored value as it is. Brand names can stay in code,
|
|
59
|
+
or become resources marked not translatable.
|
|
40
60
|
|
|
41
61
|
## 2. Android rules
|
|
42
62
|
|
|
43
63
|
- Put strings in `res/values/strings.xml` of the module that uses them. Create the file if needed:
|
|
44
64
|
`<?xml version="1.0" encoding="utf-8"?>` then `<resources>…</resources>`.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
65
|
+
Keep a default for every resource the app uses; a definition only in `values-en` is not a default.
|
|
66
|
+
Check the merged resources with Android lint for the relevant build variants, especially when
|
|
67
|
+
libraries, flavors or multiple source sets supply defaults.
|
|
68
|
+
- Key names and comments: follow the project's existing resources. If there are none, use
|
|
69
|
+
`screen_role` in snake_case (`settings_title`, `settings_save_button`) with a comment above each
|
|
70
|
+
string saying where it appears and what placeholders mean
|
|
71
|
+
(`<!-- Settings screen: saves changes. %1$s is the profile name -->`), and say so in your report.
|
|
72
|
+
Reuse an existing key when the text *and* meaning are the same; otherwise make a new one.
|
|
49
73
|
- Escape `'` as `\'` and `"` as `\"`; `&` as `&`.
|
|
50
74
|
- **Never concatenate** text: one string with numbered placeholders (`%1$s`, `%2$d`) per sentence,
|
|
51
75
|
so translators can reorder.
|
|
52
76
|
- **Counts use plurals:** `<plurals name="…"><item quantity="one">%d item</item><item quantity="other">%d items</item></plurals>`.
|
|
53
77
|
- Compose: `stringResource(R.string.key)`, `stringResource(R.string.key, arg)`,
|
|
54
78
|
`pluralStringResource(R.plurals.key, count, count)`.
|
|
55
|
-
- Outside composables: `context.getString(…)` / `resources.getQuantityString(…)`.
|
|
56
|
-
|
|
79
|
+
- Outside composables: `context.getString(…)` / `resources.getQuantityString(…)`. For text built
|
|
80
|
+
in view models or other non-UI code, follow the pattern the project already uses. If it has
|
|
81
|
+
none, expose `@StringRes` ids or a small `UiText` wrapper resolved in the UI rather than passing
|
|
82
|
+
a `Context`, and say so in your report.
|
|
57
83
|
- Money, numbers and dates go through formatters (`NumberFormat`, `DateTimeFormatter.ofLocalizedDate`),
|
|
58
|
-
never hand-built strings.
|
|
84
|
+
never hand-built strings. Keep the amount signed and set its actual currency; changing the display
|
|
85
|
+
language must not change the currency. Respect the user's regional and numbering preferences.
|
|
59
86
|
- Brand names that must not change: `<string name="app_name" translatable="false">…</string>`.
|
|
87
|
+
- Check `android:supportsRtl`, start/end spacing and alignment, and directional icons. Mirror
|
|
88
|
+
directional navigation affordances when appropriate; do not mirror every image or chart blindly.
|
|
89
|
+
- Localship handles plain locale folders. Qualified string resources such as `values-ar-night`
|
|
90
|
+
remain separate; a coverage warning must be resolved with platform checks, not by moving those
|
|
91
|
+
strings into a plain locale folder or removing the qualifiers.
|
|
60
92
|
|
|
61
93
|
## 3. iOS rules
|
|
62
94
|
|
|
63
|
-
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
`
|
|
67
|
-
|
|
95
|
+
- **Generated projects:** when `project.yml` (XcodeGen) or `Project.swift` (Tuist) exists, make
|
|
96
|
+
every project change there and regenerate; never edit the `.pbxproj` or rely on changes made in
|
|
97
|
+
Xcode, which regeneration drops. In XcodeGen that's `options: developmentLanguage: <lang>` and,
|
|
98
|
+
under `settings: base:`, `SWIFT_EMIT_LOC_STRINGS: YES` and
|
|
99
|
+
`LOCALIZATION_PREFERS_STRING_CATALOGS: YES`, with each catalog inside a source folder of its
|
|
100
|
+
target.
|
|
101
|
+
- Every target that shows text (the app, widgets and other extensions) needs its own catalog in
|
|
102
|
+
its sources.
|
|
103
|
+
- Use String Catalogs and the project's actual development language; never assume English.
|
|
104
|
+
Reuse existing tables. For a new catalog, use Xcode's localization preparation tools when
|
|
105
|
+
available, or create `Localizable.xcstrings` in the appropriate target with `sourceLanguage`
|
|
106
|
+
matching the development language, an empty `strings` object and `version: "1.0"`.
|
|
107
|
+
Verify target membership and resource inclusion, including extensions and Swift packages.
|
|
108
|
+
- Verify **Use Compiler to Extract Swift Strings** (`SWIFT_EMIT_LOC_STRINGS`) for the relevant
|
|
109
|
+
targets. Build all relevant schemes/targets and supported platforms before translating;
|
|
110
|
+
building only the main app may miss platform-conditional strings and extension resources.
|
|
111
|
+
- **Filling the catalog:** a command-line build (`xcodebuild`) writes `.stringsdata` files into
|
|
112
|
+
DerivedData but leaves the `.xcstrings` unchanged; only Xcode's catalog editor merges them. An
|
|
113
|
+
empty catalog after a clean build doesn't mean extraction failed. Sync each catalog from its
|
|
114
|
+
own target's build output:
|
|
115
|
+
```sh
|
|
116
|
+
xcrun xcstringstool sync <Target>/Localizable.xcstrings \
|
|
117
|
+
--stringsdata <DerivedData>/Build/Intermediates.noindex/<Project>.build/<Config>-<sdk>/<Target>.build/Objects-normal/*/*.stringsdata
|
|
118
|
+
```
|
|
119
|
+
- **Keys with no words** (`" "`, `"·"`, `"%@%@"`, `"—"`) come from text built in pieces.
|
|
120
|
+
Localship skips them; mark them `"shouldTranslate" : false` in the catalog so Xcode's progress
|
|
121
|
+
agrees, and check whether each one is a sentence split into pieces that should be rewritten.
|
|
122
|
+
- For copy-heavy models (personalities, tips, onboarding pages), changing an initializer
|
|
123
|
+
parameter from `String` to `LocalizedStringResource` extracts all the copy passed to it.
|
|
68
124
|
- `Text("literal")`, `Button("literal")`, `Label("literal", …)` are extracted by Xcode
|
|
69
|
-
automatically. Add a comment where the meaning isn't obvious: `Text("Save", comment: "Saves the
|
|
70
|
-
-
|
|
71
|
-
|
|
125
|
+
automatically. Add a comment where the meaning isn't obvious: `Text("Save", comment: "Saves the profile")`.
|
|
126
|
+
- Prefer `LocalizedStringResource` for passing localizable text through models and custom views;
|
|
127
|
+
use `String(localized:)` where a resolved string is needed. Avoid caching resolved translations
|
|
128
|
+
across language changes. `LocalizedStringKey` also works for SwiftUI-specific view APIs.
|
|
129
|
+
- Specify the right table and bundle for non-default catalogs, frameworks and packages.
|
|
130
|
+
On toolchains supporting it, `#bundle` refers to the current target's resources; otherwise use
|
|
131
|
+
the appropriate bundle, such as `Bundle.module` in a Swift package, rather than assuming `.main`.
|
|
132
|
+
- Xcode 26+ can generate typed `LocalizedStringResource` symbols for manually managed keys;
|
|
133
|
+
respect an existing symbol-based workflow. It also offers automatic translator comments;
|
|
134
|
+
review generated comments and add app-specific meaning and placeholder context.
|
|
72
135
|
- Use `Text(verbatim:)` for text that must never be translated (codes, user content).
|
|
73
136
|
- Interpolations become placeholders automatically: `Text("\(count) items")` → key `%lld items`.
|
|
74
|
-
Counts need plural forms:
|
|
75
|
-
|
|
137
|
+
Counts need plural forms: after syncing, find the keys with count placeholders (`%lld`,
|
|
138
|
+
`%d`) and give them plural variations (**Vary by Plural** in Xcode, or `variations.plural` in
|
|
139
|
+
the catalog JSON with the forms the source language needs). Whatever you can't convert, list
|
|
140
|
+
in your report so the user can do it in Xcode before translating.
|
|
141
|
+
Multiple independent counts require substitutions; device wording may require device variants.
|
|
142
|
+
Localship cannot translate or validate those advanced entries yet. Keep their structure and
|
|
143
|
+
use Xcode to handle them; never flatten them to make Localship's checks pass.
|
|
76
144
|
- Money, numbers and dates: `.formatted()` and `FormatStyle`, never hand-built strings.
|
|
77
|
-
-
|
|
145
|
+
- Include user-facing permission descriptions and other localizable Info.plist values through
|
|
146
|
+
`InfoPlist.xcstrings`, plus storyboard/xib and App Intents text where the app uses them.
|
|
147
|
+
- Xcode 27 has native agent localization and preview tools. When available, use them for project
|
|
148
|
+
preparation and visual review, keeping the same glossary and voice guidance. If they edit
|
|
149
|
+
translations tracked by Localship, review the resulting hand edits before adopting them.
|
|
78
150
|
|
|
79
151
|
## 4. Check nothing was missed
|
|
80
152
|
|
|
81
|
-
- The app must build after each chunk. Don't change logic, layout or behavior.
|
|
153
|
+
- The app must build after each chunk. Don't change logic, layout or behavior. Run Android lint
|
|
154
|
+
for affected variants, or build the affected Apple targets/platforms; file validation alone
|
|
155
|
+
cannot verify resource merging, extraction or runtime lookups.
|
|
82
156
|
- Android: enable pseudolocales (`android { buildTypes { debug { isPseudoLocalesEnabled = true } } }`),
|
|
83
157
|
switch the device to "English (XA)", and look for text that isn't accented: that text is still hardcoded.
|
|
84
158
|
"AR (XB)" shows right-to-left layout problems.
|
|
85
159
|
- iOS: Scheme › Run › Options › App Language › "Accented Pseudolanguage" (and "Right-to-Left
|
|
86
|
-
Pseudolanguage"). Plain English left over was missed.
|
|
87
|
-
|
|
160
|
+
Pseudolanguage"). Plain English left over was missed. Without Xcode's UI, a scripted check works:
|
|
161
|
+
add a temporary language whose values wrap the English in markers (`⟦Save⟧`), launch with
|
|
162
|
+
`-AppleLanguages "(xx)"`, look for text without markers, then remove the temporary language.
|
|
163
|
+
- If you can't run these, list the screens for the user to check.
|
|
88
164
|
|
|
89
165
|
### Text the resource files can't show
|
|
90
166
|
|
|
@@ -94,21 +170,29 @@ for them while you're in it:
|
|
|
94
170
|
- **Names stored in data.** Categories, accounts or templates seeded in English and saved to a
|
|
95
171
|
database are shown as stored. Translate them at display time while they're still the seeded
|
|
96
172
|
value (a map from seeded name to string resource); never translate what the user typed.
|
|
173
|
+
Use the translated name only for showing it: anything saved (database rows, defaults,
|
|
174
|
+
exports, a name copied into a new record) keeps the stored value, or it stays in whatever
|
|
175
|
+
language the app had when it was saved.
|
|
97
176
|
- **Text built once and kept.** View models, singletons and caches that build sentences keep the
|
|
98
177
|
old language after an in-app language switch. Rebuild them when the locale changes; on Android,
|
|
99
178
|
view models survive the activity being recreated for the new language.
|
|
100
|
-
- **Charts drawn by hand.**
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
179
|
+
- **Charts drawn by hand.** Check that custom drawing and surrounding labels agree in RTL.
|
|
180
|
+
Decide mirroring based on the chart's meaning and axis conventions, and test it on a device.
|
|
181
|
+
- **Signs and symbols on amounts.** Format the signed value as one unit with the platform's
|
|
182
|
+
number/currency formatter. Preserve its Unicode direction marks. Do not prepend a minus sign,
|
|
183
|
+
strip U+200F or force every amount to LTR. For dynamic text inserted into an Android localized
|
|
184
|
+
message, use context-aware `BidiFormatter.unicodeWrap()` unless already wrapped. On Apple,
|
|
185
|
+
use localized interpolation and natural text direction. Test positive/negative amounts,
|
|
186
|
+
currency symbols and mixed-script text in the actual UI before adjusting directionality.
|
|
187
|
+
- **Dates and months.** Check that language switching refreshes labels while formatters respect
|
|
188
|
+
intended regional preferences; language, region, calendar and currency are separate choices.
|
|
189
|
+
|
|
190
|
+
Use the [platform verification guide](references/platform-localization.md) for current official
|
|
191
|
+
references and the final test checklist.
|
|
109
192
|
|
|
110
193
|
## 5. Then
|
|
111
194
|
|
|
112
|
-
Run `localship detect` (or `localship_detect`): it should now find the strings.
|
|
113
|
-
|
|
114
|
-
|
|
195
|
+
Run `localship detect` (or `localship_detect`): it should now find the strings. Report what you
|
|
196
|
+
moved, and any convention you had to pick. If the user also asked for translation, continue with
|
|
197
|
+
the `localship` skill. Don't create branches or commit unless the user asks; if they do commit,
|
|
198
|
+
suggest committing the extraction on its own, so the diff is easy to review.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Platform verification
|
|
2
|
+
|
|
3
|
+
Guidance checked against official Android and Apple documentation on October 17, 2026.
|
|
4
|
+
Check the project's installed toolchain and deployment targets before applying newer APIs.
|
|
5
|
+
|
|
6
|
+
## Android
|
|
7
|
+
|
|
8
|
+
- Keep all needed default strings in unqualified `values` resources. Verify resource merging
|
|
9
|
+
and references with the relevant Gradle lint/build tasks. A Localship target is a resource
|
|
10
|
+
directory, not a model of every dependency, flavor and source set in the APK.
|
|
11
|
+
- Plain locale folders are supported. Strings in other configurations are preserved and reported
|
|
12
|
+
as uncovered; `--strict` and `--require-approved` fail for those files. Do not remove qualifiers
|
|
13
|
+
or copy conditional overrides into base resources to silence this diagnostic.
|
|
14
|
+
- Test supported languages with the system and any in-app picker, including persistence and
|
|
15
|
+
switching while screens and view models remain alive.
|
|
16
|
+
- Format signed currency values using their actual currency and the intended display locale.
|
|
17
|
+
Preserve direction marks produced by formatters; use bidi-aware handling for embedded text.
|
|
18
|
+
|
|
19
|
+
Official references:
|
|
20
|
+
|
|
21
|
+
- [Localization and default resources](https://developer.android.com/guide/topics/resources/localization)
|
|
22
|
+
- [String resources and plurals](https://developer.android.com/guide/topics/resources/string-resource)
|
|
23
|
+
- [Per-app language setup and migration](https://developer.android.com/guide/topics/resources/app-languages)
|
|
24
|
+
- [RTL, layout mirroring and BidiFormatter](https://developer.android.com/training/basics/supporting-devices/languages)
|
|
25
|
+
- [Pseudolocales](https://developer.android.com/guide/topics/resources/pseudolocales)
|
|
26
|
+
|
|
27
|
+
## Apple platforms
|
|
28
|
+
|
|
29
|
+
- Use the actual development language, correct table/bundle, and compiler extraction. Build
|
|
30
|
+
relevant targets and platforms before treating catalogs as complete; include extensions,
|
|
31
|
+
packages, Info.plist text and interface resources used by the app.
|
|
32
|
+
- Command-line builds write `.stringsdata` but don't update catalogs; sync them with
|
|
33
|
+
`xcrun xcstringstool sync`. In XcodeGen or Tuist projects, make localization settings in
|
|
34
|
+
`project.yml` or `Project.swift`, since regenerating drops changes made in Xcode.
|
|
35
|
+
- Keep device variants and substitutions intact. Localship currently models plain strings and
|
|
36
|
+
one level of plural string units. Advanced entries remain in the catalog but block strict
|
|
37
|
+
completeness checks. Handle them in Xcode and report that Localship cannot certify the whole
|
|
38
|
+
catalog; do not set `shouldTranslate: false` merely to bypass validation.
|
|
39
|
+
- Generated symbols are optional: preserve the project's chosen extraction/manual-key workflow.
|
|
40
|
+
Review generated comments for accurate context. Native Xcode agent tools can prepare catalogs
|
|
41
|
+
and preview translations; align them with the project's terminology and review their edits.
|
|
42
|
+
|
|
43
|
+
Official references:
|
|
44
|
+
|
|
45
|
+
- [String Catalogs, plurals and device variants](https://developer.apple.com/documentation/Xcode/localizing-and-varying-text-with-a-string-catalog)
|
|
46
|
+
- [Compiler extraction, bundles and substitutions](https://developer.apple.com/videos/play/wwdc2023/10155/)
|
|
47
|
+
- [Xcode 26 comments, symbols and resource bundles](https://developer.apple.com/videos/play/wwdc2025/225/)
|
|
48
|
+
- [Generated localizable symbols](https://developer.apple.com/documentation/xcode/using-generated-localizable-symbols-in-your-code)
|
|
49
|
+
- [Xcode 27 agent localization and visual review](https://developer.apple.com/videos/play/wwdc2026/213/)
|
|
50
|
+
|
|
51
|
+
## Release verification
|
|
52
|
+
|
|
53
|
+
1. Run Localship validation; review errors, warnings and coverage gaps. After approving the
|
|
54
|
+
selected locales, run `localship validate --require-approved --locale <locales>`.
|
|
55
|
+
2. Run the native build and applicable Android lint tasks. Resolve missing defaults and wrong
|
|
56
|
+
target/bundle membership. File validation cannot replace these checks.
|
|
57
|
+
3. Exercise pseudolocales and actual languages: long text, tall scripts, RTL and large system
|
|
58
|
+
text sizes. Look for clipping, incorrect alignment, inaccessible labels and layout changes.
|
|
59
|
+
4. Check plural boundaries appropriate to each locale, signed/zero amounts, mixed-script names,
|
|
60
|
+
and date/calendar/number formatting. Preserve user-entered content.
|
|
61
|
+
5. Test language switching, navigation, notifications, widgets and cached/seeded labels where
|
|
62
|
+
the app has them. Get native-speaker feedback on meaning and tone.
|
|
63
|
+
|
|
64
|
+
Report which checks ran and which could not. Approval records do not prove linguistic quality,
|
|
65
|
+
and a passing check for supported files does not establish correct runtime behavior.
|
|
@@ -50,4 +50,12 @@ installed, ask to install it once with `npm install -g localship`.
|
|
|
50
50
|
- `--from edited` accepts hand edits; `--from untracked` adopts translations made before
|
|
51
51
|
the tool.
|
|
52
52
|
|
|
53
|
-
Run `localship validate` at the end; it must have no errors.
|
|
53
|
+
Run `localship validate` at the end; it must have no errors. Report coverage warnings separately:
|
|
54
|
+
unsupported variants are not reviewed just because the supported entries passed. After approval,
|
|
55
|
+
use `localship validate --require-approved --locale <reviewed-locales>` for the release check.
|
|
56
|
+
Do not auto-approve or flatten unsupported entries to satisfy that check.
|
|
57
|
+
|
|
58
|
+
For a release, follow the [platform verification checklist](../localship-extract/references/platform-localization.md):
|
|
59
|
+
native build/lint, pseudolocales, large text, RTL, language switching and native-speaker review.
|
|
60
|
+
Use Xcode preview tools when available for visual checks, and report what still needs a device.
|
|
61
|
+
Commit nothing unless asked.
|
|
@@ -9,8 +9,8 @@ Localship keeps translations in the repo's own files (Android `res/values-*/stri
|
|
|
9
9
|
gettext `.po`, Apple `.xcstrings`) and checks every translation in code. You do the parts
|
|
10
10
|
that need judgment: choosing what to set up, and writing the context translators use.
|
|
11
11
|
|
|
12
|
-
> For a direct request ("translate this app to Hindi"), start with the `localship` skill:
|
|
13
|
-
>
|
|
12
|
+
> For a direct request ("translate this app to Hindi"), start with the `localship` skill instead:
|
|
13
|
+
> it sets up only the config and translates.
|
|
14
14
|
|
|
15
15
|
## Running the tool
|
|
16
16
|
|
|
@@ -19,9 +19,6 @@ that need judgment: choosing what to set up, and writing the context translators
|
|
|
19
19
|
`npm install -g localship` (running it through `npx` every time is slow).
|
|
20
20
|
- Add `--json` when you need to read the output.
|
|
21
21
|
|
|
22
|
-
Before writing anything, check `git status`. If there are uncommitted changes, mention it, so the
|
|
23
|
-
user can tell your changes apart from theirs.
|
|
24
|
-
|
|
25
22
|
## 1. Find out where the project stands
|
|
26
23
|
|
|
27
24
|
Run `localship status`. If it works, the project is already set up (if it warns that most text is still in the code, go to case C): go to step 5 (context) if
|
|
@@ -30,20 +27,19 @@ pick the case that fits:
|
|
|
30
27
|
|
|
31
28
|
| What detect shows | Case |
|
|
32
29
|
|---|---|
|
|
33
|
-
| A warning that most of the app's text is still in the code (more UI text in code than strings in resources) | **C. Text is hardcoded**, even if a few strings exist:
|
|
30
|
+
| A warning that most of the app's text is still in the code (more UI text in code than strings in resources) | **C. Text is hardcoded**, even if a few strings exist: tell the user how much is in code; translations cover only what's in resources until it's moved (the `localship-extract` skill, if they ask) |
|
|
34
31
|
| Setups with existing languages (`hi, ar` …) | **A. Already localized** |
|
|
35
32
|
| Setups with strings but no languages | **B. Ready to translate** |
|
|
36
|
-
| An empty String Catalog (0 strings) |
|
|
33
|
+
| An empty String Catalog (0 strings) | Check compiler extraction and target membership, build the relevant targets and platforms, and sync the catalog from the build (`xcrun xcstringstool sync`; a command-line build alone leaves it empty, see `localship-extract`), then detect again |
|
|
37
34
|
| A gettext template with no translations | **B**, after confirming where new `.po` files go (detect suggests a path) |
|
|
38
|
-
| Nothing, with a hint about Android or Xcode | **C. Text is hardcoded**:
|
|
35
|
+
| Nothing, with a hint about Android or Xcode | **C. Text is hardcoded**: nothing to set up until the text is in resources; the `localship-extract` skill moves it, if the user asks |
|
|
39
36
|
| Only formats listed as "not supported yet" (Flutter ARB, JSON, `.strings` …) | Say so plainly; Localship can't translate those yet |
|
|
40
37
|
|
|
41
|
-
## 2.
|
|
38
|
+
## 2. Init
|
|
42
39
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
- **case B:** which languages to translate into. Suggest from their market if they don't know.
|
|
40
|
+
Include every setup detect found except debug, test and sample folders, unless the user said
|
|
41
|
+
otherwise. Languages: the ones the user named, plus (case A) the ones already there. If the user
|
|
42
|
+
named none and there are none, ask which; that's the one thing setup can't guess.
|
|
47
43
|
|
|
48
44
|
Then run `localship init --accept <numbers> --locales <list>` (add `--source <locale>` if the detected
|
|
49
45
|
source language is wrong). Language codes are BCP 47 (`hi`, `ar`, `pt-BR`, `zh-Hans`). Never pass
|
|
@@ -51,40 +47,57 @@ source language is wrong). Language codes are BCP 47 (`hi`, `ar`, `pt-BR`, `zh-H
|
|
|
51
47
|
|
|
52
48
|
## 3. Existing translations (case A)
|
|
53
49
|
|
|
54
|
-
They show as `untracked`: made before Localship, so the tool doesn't know if they're good.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
-
|
|
58
|
-
|
|
59
|
-
plurals). Report the errors and offer to fix them.
|
|
50
|
+
They show as `untracked`: made before Localship, so the tool doesn't know if they're good. Leave
|
|
51
|
+
them as they are; they're never overwritten without the user's say-so. In your report, mention
|
|
52
|
+
that the user can approve them (`localship approve --from untracked`) or have them reviewed (the
|
|
53
|
+
`localship-review` skill). Run `localship validate`: existing files often have real problems
|
|
54
|
+
(missing placeholders, broken plurals). Report the errors; fix them if the user asks.
|
|
60
55
|
|
|
61
56
|
## 4. Platform setup the tool can't do (mention what applies)
|
|
62
57
|
|
|
63
|
-
- Android:
|
|
64
|
-
`androidResources { generateLocaleConfig = true }` and
|
|
65
|
-
`app
|
|
66
|
-
|
|
58
|
+
- Android: inspect AGP, compileSdk and any existing language configuration first. Automatic
|
|
59
|
+
`androidResources { generateLocaleConfig = true }` needs AGP 8.1+ and compileSdk 33+.
|
|
60
|
+
Set `unqualifiedResLocale` in the app module's `res/resources.properties` to the actual
|
|
61
|
+
default locale, not an assumed `en-US`. Do not combine a manual LocaleConfig with generation;
|
|
62
|
+
reconcile the existing setup. Check the generated supported-language list, including library
|
|
63
|
+
resources, and restrict it to languages ready for release using the installed AGP's supported
|
|
64
|
+
locale-filter configuration.
|
|
65
|
+
- Android in-app selection: integrate platform `LocaleManager` on Android 13+ or compatible
|
|
66
|
+
AndroidX `AppCompatDelegate.setApplicationLocales`; consider the Activity/context requirements
|
|
67
|
+
on older devices, persisted preferences and migration from any existing picker. Test system
|
|
68
|
+
settings, app settings, restart and configuration changes together. Generating LocaleConfig
|
|
69
|
+
alone does not implement a backward-compatible picker.
|
|
70
|
+
- iOS: add languages through the catalog/project localization settings or available Xcode
|
|
71
|
+
preparation tools. Check the development language, resource bundles, compiler extraction and
|
|
72
|
+
all relevant targets/platforms, as described in `localship-extract`. Xcode 27's native agent
|
|
73
|
+
tools can assist preparation; use them when available, with the project's glossary and voice.
|
|
74
|
+
|
|
75
|
+
Read [platform verification](../localship-extract/references/platform-localization.md) for the
|
|
76
|
+
official references. Report unsupported catalog/resource configurations before translating;
|
|
77
|
+
strict validation blocks them even when supported strings are all complete.
|
|
67
78
|
|
|
68
79
|
## 5. Write the context
|
|
69
80
|
|
|
70
|
-
|
|
71
|
-
listing, website copy and the strings themselves,
|
|
81
|
+
The user asked for setup, so write these in `.localship/` (the `context` folder in the config).
|
|
82
|
+
Draft them from the README, store listing, website copy and the strings themselves, and show the
|
|
83
|
+
user what you wrote. Don't overwrite files that already exist.
|
|
72
84
|
|
|
73
85
|
- `brief.md`: what the app does, who uses it, the tone of its copy (calm, playful, formal…), and
|
|
74
|
-
anything translators get wrong without context (e.g. "a '
|
|
75
|
-
|
|
86
|
+
anything translators get wrong without context (e.g. "a 'notebook' is a group of notes, not
|
|
87
|
+
a paper notebook"). Half a page at most.
|
|
76
88
|
- `voice/all.md`: voice rules for every language. `voice/<locale>.md`: per language, e.g. formality
|
|
77
|
-
(German du vs Sie, Hindi तुम vs आप), English loanwords, number and date conventions.
|
|
78
|
-
|
|
89
|
+
(German du vs Sie, Hindi तुम vs आप), English loanwords, number and date conventions. Use what the
|
|
90
|
+
user said; otherwise the usual convention for consumer apps in that language, and name the choice
|
|
91
|
+
in your summary so they can change it.
|
|
79
92
|
- `glossary.yaml`: brand and product names to keep (`translate: false`), and product terms that
|
|
80
93
|
must be translated consistently:
|
|
81
94
|
```yaml
|
|
82
95
|
terms:
|
|
83
|
-
- term:
|
|
96
|
+
- term: Jotter
|
|
84
97
|
translate: false
|
|
85
|
-
- term:
|
|
86
|
-
note:
|
|
87
|
-
translations: { hi:
|
|
98
|
+
- term: notebook
|
|
99
|
+
note: A named group of notes, not a paper notebook
|
|
100
|
+
translations: { hi: नोटबुक }
|
|
88
101
|
```
|
|
89
102
|
Keep it short: names, and terms that appear in many strings or carry product meaning.
|
|
90
103
|
|
|
@@ -93,7 +106,8 @@ Without file access, use `localship_update_context` instead.
|
|
|
93
106
|
## Cleaning up
|
|
94
107
|
|
|
95
108
|
After strings are deleted or renamed, `localship prune --dry-run` shows leftover translations; show
|
|
96
|
-
the user, then run `localship prune`.
|
|
109
|
+
the user, then run `localship prune`. First verify that the source keys were actually removed:
|
|
110
|
+
a missing Android default can look like a leftover translation, and should be restored instead.
|
|
97
111
|
|
|
98
112
|
## Adding languages later
|
|
99
113
|
|
|
@@ -101,5 +115,5 @@ the user, then run `localship prune`.
|
|
|
101
115
|
|
|
102
116
|
## Finish
|
|
103
117
|
|
|
104
|
-
Run `localship validate` and `localship status`, and tell the user the next step: the
|
|
105
|
-
|
|
118
|
+
Run `localship validate` and `localship status`, and tell the user the next step: translating (the
|
|
119
|
+
`localship-translate` skill). Commit nothing unless the user asks.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: localship-translate
|
|
3
|
-
description: Translate an app's missing or changed strings with Localship — fetch batches with context, translate them, submit them for checks, and fix what's rejected until nothing is left. Use
|
|
3
|
+
description: Translate an app's missing or changed strings with Localship — fetch batches with context, translate them, submit them for checks, and fix what's rejected until nothing is left. Use for the translating itself, once the text is in string resources; for a request to localize the app or add a language, start with the localship skill, which works out whether text has to be moved first.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Translate with Localship
|
|
@@ -10,7 +10,7 @@ The tool picks the strings, gives you the context, checks every translation in c
|
|
|
10
10
|
You translate. Don't edit translation files by hand.
|
|
11
11
|
|
|
12
12
|
> For a direct request ("translate this app to Hindi"), start with the `localship` skill: it
|
|
13
|
-
>
|
|
13
|
+
> checks the project, then uses this playbook.
|
|
14
14
|
|
|
15
15
|
## Running the tool
|
|
16
16
|
|
|
@@ -19,23 +19,23 @@ You translate. Don't edit translation files by hand.
|
|
|
19
19
|
`npm install -g localship`.
|
|
20
20
|
- No `.localship/config.yaml`? Run the `localship-setup` skill first.
|
|
21
21
|
- A language that isn't configured yet: `localship add-locale <code>` first.
|
|
22
|
-
- Check `git status` first and mention uncommitted changes, so the translation diff stays reviewable.
|
|
23
22
|
|
|
24
23
|
## 1. Size up the work
|
|
25
24
|
|
|
26
25
|
Run `localship status`. Count `missing` + `stale` across the locales to translate.
|
|
26
|
+
Read the status notes too. Unsupported Apple variations and Android configurations are excluded
|
|
27
|
+
from the counts; report them as uncovered work and preserve their structure. Zero remaining
|
|
28
|
+
batch entries does not mean those strings have been translated or validated.
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
It runs the same checks and retries rejected strings. Then review what it reports as failed
|
|
34
|
-
and translate those yourself.
|
|
30
|
+
Translate them yourself (step 2), however many there are. Use a model API
|
|
31
|
+
(`localship translate --model <provider:model>`) only when the user asks for it: cloud providers
|
|
32
|
+
(`anthropic`, `openai`, `gemini`, `openrouter`) bill per use and need their API key; local
|
|
33
|
+
`ollama` and `lmstudio` are free. It runs the same checks; translate what it reports as failed
|
|
34
|
+
yourself.
|
|
35
35
|
|
|
36
|
-
Read `.localship/brief.md`, `.localship/voice/` and `.localship/glossary.yaml` once if they exist.
|
|
37
|
-
there's no brief,
|
|
38
|
-
|
|
36
|
+
Read `.localship/brief.md`, `.localship/voice/` and `.localship/glossary.yaml` once if they exist.
|
|
37
|
+
If there's no brief, read the README for what the app does and its tone; don't write a brief
|
|
38
|
+
unless the user asks.
|
|
39
39
|
|
|
40
40
|
## 2. The loop
|
|
41
41
|
|
|
@@ -51,7 +51,8 @@ come out generic.
|
|
|
51
51
|
- `editedByHand: true` — a person edited the current translation. Keep their wording where you
|
|
52
52
|
can, and show the user your proposed change: `submit` refuses it until they agree (then pass
|
|
53
53
|
`--force` for just those strings).
|
|
54
|
-
The batch also carries the `brief`, each locale's `voice
|
|
54
|
+
The batch also carries the `brief`, each locale's `voice` (or `defaultVoice`, the usual
|
|
55
|
+
convention, when the project has set none), and notes per format.
|
|
55
56
|
|
|
56
57
|
2. Translate every item following these rules:
|
|
57
58
|
- Natural, concise UI language a native speaker would write, in the brief's tone and the
|
|
@@ -69,6 +70,9 @@ come out generic.
|
|
|
69
70
|
- When the source could mean two things ("Open" as a button or a status, "Balance" as money
|
|
70
71
|
or equilibrium) and the context doesn't settle it, add a short `note` saying how you read
|
|
71
72
|
it. Reviewers see it. Skip notes when the meaning is clear.
|
|
73
|
+
- Same source text, different meaning ("Delete account" for a sign-in account and for a
|
|
74
|
+
budget): translate each for its meaning and add a `note` saying how they differ, so the
|
|
75
|
+
consistency check doesn't flag it.
|
|
72
76
|
- Android values: write plain text; apostrophes, quotes and `&` are escaped for you.
|
|
73
77
|
|
|
74
78
|
3. Submit, using a file for anything non-trivial:
|
|
@@ -82,7 +86,7 @@ come out generic.
|
|
|
82
86
|
]}
|
|
83
87
|
```
|
|
84
88
|
Copy `target`, `locale` and `key` from the batch items. Add `"note"` to an entry only for an
|
|
85
|
-
ambiguous source. Delete the file afterwards.
|
|
89
|
+
ambiguous source, or one worded differently on purpose. Delete the file afterwards.
|
|
86
90
|
|
|
87
91
|
4. Read the result. Rejected entries list their errors: fix exactly those and resubmit only
|
|
88
92
|
them. Accepted entries may have warnings (glossary, untranslated, whitespace): fix the
|
|
@@ -93,15 +97,19 @@ come out generic.
|
|
|
93
97
|
## 3. Finish
|
|
94
98
|
|
|
95
99
|
- Run `localship validate`. It must have no errors.
|
|
100
|
+
- Resolve or explicitly report coverage warnings. `validate --strict` checks completeness;
|
|
101
|
+
after review and approval, `validate --require-approved` also enforces approval. Unsupported
|
|
102
|
+
content blocks both checks; use the platform tools for that content and report the limitation.
|
|
96
103
|
- Run `localship status` and report per locale: how many drafts were written, anything that
|
|
97
|
-
failed, warnings you left and why, and terms you were unsure of.
|
|
104
|
+
failed, warnings you left and why, and terms you were unsure of. Say which voice you followed
|
|
105
|
+
(especially a `defaultVoice`) so the user can change it.
|
|
98
106
|
- **Don't approve your own drafts.** Suggest the user reviews them (the `localship-review` skill),
|
|
99
107
|
then `localship approve`.
|
|
100
108
|
- For a right-to-left language (Arabic, Hebrew, Persian, Urdu), or any first translation, tell the
|
|
101
109
|
user what still needs a device: the layout mirrored, long text cut off, plurals at 0, 1, 2, 5
|
|
102
110
|
and 11, switching language while the app is open, charts, amounts with a sign, and names
|
|
103
|
-
stored in data.
|
|
104
|
-
|
|
111
|
+
stored in data. Localship's checks only cover the translation files; the `localship-extract`
|
|
112
|
+
skill lists where to look in the code if the user wants that checked.
|
|
105
113
|
- Never pass `--force` unless the user explicitly asks: it replaces translations a person
|
|
106
114
|
wrote, reviewed or approved.
|
|
107
115
|
|