• Stars
    star
    97
  • Rank 345,811 (Top 7 %)
  • Language
    Swift
  • License
    MIT License
  • Created about 3 years ago
  • Updated 18 days ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Validate iOS, Android, and Mac localizations. Find errors in .strings, .stringsdict, and strings.xml files.

Locheck

Swift 5.7 License Build

An Xcode and Android localization file validator. Make sure your .strings, .stringsdict, and strings.xml files do not have any errors!

What does it do?

Locheck can perform many kinds of checks on localization files. The simplest one is making sure all strings appear in both the base language and translations, but it can also make sure all your format specifiers are consistent, even in .stringsdict files.

Consider this string:

"Send %d donuts to %@" = "%@ to donuts %d send";
<!-- values/strings.xml -->
<string name="send_donuts">Send %d donuts to %s</string>
<!-- values-translation/strings.xml -->
<string name="send_donuts">%s to donuts %d send</string>

The translation reads naturally on its own, but this would crash your app when iOS or Android tries to format a number as a string and a string as a number. Instead, the translation should look like this:

"Send %d donuts to %@" = "%2$@ to donuts %1$d send";
<!-- values-translation/strings.xml -->
<string name="send_donuts">%2$s to donuts %1$d send</string>

Locheck will make sure you get it right.

In the example above, the key happens to be equal to the base translation. But you might have special cases where you manually define your string in Localizable.strings, so the key's format string doesn't match the value:

// in en.lproj/Localizable.strings:
"send-donuts" = "Send %d donuts to %@";

// in backwards.lproj/Localizable.strings:
"send-donuts" = "%@ to donuts %d send";

In these cases, Locheck will use the base translation's value (not its key) as the authoritative string, and would catch the error in the example above.

Installation

Manual

git clone [email protected]:Asana/locheck.git
cd locheck
make install

Using Mint

mint install Asana/locheck
mint run locheck [...]

# or link it to /usr/local/bin
mint install Asana/locheck --link
locheck [...]

Locheck is not yet popular enough to be in homebrew/core and we haven't created a tap yet.

Usage

There are a few ways to invoke locheck depending on how much magic you want. In all cases, Locheck will write to stderr for Xcode integration, and stdout for a human-readable summary. Pull requests for additional output formats will probably be accepted.

discoverlproj

The simplest way to use Locheck with Xcode is to use discoverlproj and point to a directory containing all your .lproj files:

locheck discoverlproj "MyApp/Supporting Files" --base en # use English as the base language

If you use a language besides English as your base, you'll need to pass it as an argument as shown in the example. Locheck does not try to read your xcodeproj file to figure it out.

discovervalues

The simplest way to use Locheck on Android is to use discovervalues and point to a directory containing all your values[-*] directories, i.e. your res/ directory.

locheck discovervalues ./app/src/main/res

Other ways

Run locheck --help to see a list of all commands. The rest of the commands just let you directly compare individual files of different types.

Example output

> locheck discovervalues $ANDROID/app/src/main/res --ignore key_missing_from_translation --ignore key_missing_from_base
Discovering values[-*]/strings.xml files in /.../app/src/main/res
Source of truth: /.../app/src/main/res/values/strings.xml
Translations to check: 12
/.../app/src/main/res/values-de/strings.xml:242: error: Translation of 'could_not_mark_as_milestone' includes arguments that don't exist in the source: task_name (string_has_extra_arguments)
/.../app/src/main/res/values-de/strings.xml:899: warning: 'organization_required_mfa_help_text' does not include argument(s): authy_url, duo_mobile_url, microsoft_authenticator_url (phrase_has_missing_arguments)
/.../app/src/main/res/values-ko/strings.xml:1403: error: Translation of 'what_are_a_few_tasks_you_have_to_do_for_project_name' includes arguments that don't exist in the source: projectName (string_has_extra_arguments)
/.../app/src/main/res/values-ko/strings.xml:1426: warning: 'created_video_phrase_template' does not include argument(s): author_name (phrase_has_missing_arguments)
[...]

Summary:
/.../app/src/main/res/values-ko/strings.xml
    could_not_mark_as_milestone:
        ERROR: Translation of 'could_not_mark_as_milestone' includes arguments that don't exist in the source: task_name
    created_video_phrase_template:
        WARNING: 'created_video_phrase_template' does not include argument(s): author_name
        ERROR: Translation of 'created_video_phrase_template' includes arguments that don't exist in the source: userName1
    organization_required_mfa_help_text:
        WARNING: 'organization_required_mfa_help_text' does not include argument(s): authy_url, duo_mobile_url, microsoft_authenticator_url
    what_are_a_few_tasks_you_have_to_do_for_project_name:
        WARNING: 'what_are_a_few_tasks_you_have_to_do_for_project_name' does not include argument(s): project_name
        ERROR: Translation of 'what_are_a_few_tasks_you_have_to_do_for_project_name' includes arguments that don't exist in the source: projectName
[...]
20 warnings, 29 errors
Ignored key_missing_from_translation, key_missing_from_base
Errors found

Contributing

GitHub issues and pull requests are very welcome! Please format your code with swiftformat Sources Tests before opening your PR, otherwise tests will fail and we cannot merge your branch. We also run SwiftLint to help ensure best practices.

The simplest way to install SwiftFormat and SwiftLint is to use Mint:

brew install mint
mint bootstrap --link`

You can then run both tools locally:

swiftformat Sources Tests
swiftlint lint --quiet

Further reading

More Repositories

1

Drawsana

An open source library that lets your users draw on things - mark up images with text, shapes, etc.
Swift
644
star
2

typed-react

A binding layer between React and TypeScript
TypeScript
373
star
3

kraken

Distributed Pubsub Server for Realtime Apps
Erlang
335
star
4

python-asana

Official Python client library for the Asana API v1
Python
280
star
5

node-asana

Official node.js and browser JS client for the Asana API v1
JavaScript
249
star
6

Chrome-Extension-Example

Sample application illustrating use of the Asana API
JavaScript
232
star
7

php-asana

Official PHP client library for the Asana API v1
PHP
135
star
8

bazels3cache

Small web server for a Bazel cache, proxies to S3; allows Bazel to work offline; async uploads to make Bazel faster
TypeScript
79
star
9

ruby-asana

Official Ruby client library for the Asana API v1
Ruby
76
star
10

bazeltsc

TypeScript compiler that knows how to run as a Bazel "persistent worker"
TypeScript
39
star
11

java-asana

Official Java client library for the Asana API v1
Java
35
star
12

create-app-attachment-github-action

TypeScript
33
star
13

devrel-examples

A place to share some examples from our Developer Relations team for commonly-asked-about workflows.
Python
27
star
14

comment-on-task-github-action

TypeScript
25
star
15

asana2sql

Utility for exporting Asana data to SQL databases
Python
23
star
16

api-explorer

React component to explore the Asana API
TypeScript
19
star
17

omniauth-asana

Official Asana strategy for OmniAuth
Ruby
16
star
18

SGTM

Python
14
star
19

asana-api-meta

Metadata for Asana API for generating client libraries and documenation
HTML
11
star
20

kraken-node-client

A nodejs client for the Kraken pubsub server
JavaScript
10
star
21

tsutil

TypeScript Utility Data Structures
TypeScript
9
star
22

node-asana-phrase

A random error phrase generator used to create memorable error codes, as used by Asana.
JavaScript
9
star
23

typescript-namespace-imports-vscode-plugin

A VSCode plugin that makes it easier to automatically include TypeScript namespace imports.
TypeScript
6
star
24

asana-shift

A small node script which uses the Asana API to shift all task start and due dates relative to a project's due date.
TypeScript
5
star
25

markdown-formatter

JavaScript
5
star
26

random-one-on-one

Python
5
star
27

app-components-example-app

app-components-example-app
JavaScript
4
star
28

ohmega

The Asana Ohmega process automation toolkit
Python
4
star
29

openapi

Python
4
star
30

sshca

Certificate authority for OpenSSH
Python
3
star
31

jira-server-plugin

Asana for Jira Server
3
star
32

node-asana-preview

A preview of Asana's new node client library
JavaScript
3
star
33

app-components-rule-action-example-app

JavaScript
3
star
34

python-asana-preview

A preview of Asana's new python client library
Python
3
star
35

node-sync-to-github

A node library that makes it easy to sync a directory of files to a GitHub repo using the GitHub API
JavaScript
3
star
36

deprovision_inactive_guests

A small script which uses the Asana API to remove external users (ie without a company email) from an organization if they haven't logged in for 30 days
JavaScript
2
star
37

archie

Python
2
star
38

formula-custom-fields

JavaScript
1
star
39

node-linux-fork

An implementation of fork() for Node.JS in Linux (requires a custom Node.JS build)
C++
1
star