Clean iOS Localizable Files
Bài đăng này đã không được cập nhật trong 6 năm
Apple đã giúp cho việc "bản địa hoá" - localized
- một ứng dụng đơn giản đi rất nhiều, tuy nhiên để các file localizable ngắn gọn và sạch sẽ - concise and clean
- lại là một câu chuyện khác, đặc biệt là khi app của bạn hỗ trợ nhiều ngôn ngữ.
Điều thường xảy ra với các file Localizable.strings
là chúng có xu hướng trở nên dài và cẩu thả 1 cách rất nhanh chóng =)) Như nhiều coder có thể chứng nhận: thêm code thì dễ dàng hơn nhiều so với việc modify hoặc edit code hiện có, và việc thêm 1 cặp key/value vào trong file localizable cũng y hệt như thế. Việc thêm 1 cặp key/value vào thì luôn dễ hơn việc tìm 1 cặp đã tồn tại để reuse. Bỏ qua cơ hội có thể sử dụng 1 cặp đang có có thể dẫn đến việc trừng lặp trong localizable file và dẫn đến các unexpected behaviors view
.
Không cần phải nói, cấu trúc của các file localizable là rất quan trọng. Một file file localizable được sắp xếp tốt không chỉ vì mục đích thẩm mỹ mà còn vì giúp cho việc tìm kiếm và lướt qua để thấy được các cặp hiện tại. Tuy nhiên, duy trì một file được sắp xếp tốt là không hiệu quả, khi các developers luôn add các cặp mới tuỳ tiện nhất là khi làm việc chung.
Một vấn đề phổ biến nữa ta thường thấy trong file localizable là việc sử dụng các key trong thực thế. Thông thường, các developer hay quên việc xoá các cặp key/value khi code đã bị xoá. Hoặc ngược lại, key vẫn được sử dụng nhưng developer quên define value trong file localizable. Điều này còn tồi tệ hơn điều trước khi nó trực tiếp gây ra 1 UI bug rất là hãm: để user thấy 1 chuỗi ký tự khó hiểu =)). Tuy nhiên cả 2 trường hợp trên vẫn rát dễ xảy ra khi project được build trên Xcode và ko bao giờ ném ra các errors hay warning nào.
Example
1. Key duplications:
2. Keys between multiple localizable files do not match:
3. Unused keys:
4. Undefined keys:
Mitigate
Find Duplicated
Để giảm thiểu những vấn đề này, chúng ta sẽ tạo ra 1 automated post build script
để clean, maintains và đưa ra các warning/errors trên Xcode cho bất kỳ 1 usage problems nào ở trên. Điều đầu tiên mà script chugns ta nên làm là tìm được tất cả các file localizable trong project, sắp xếp các key, và xác định các key bị duplications.
#!/bin/bash
project_name="CleanLocalizableExample"
es_duplicates=9
sort_and_find_duplicates() {
echo "== Sorting localizable files: $1 =="
sed -i '' '/^$/d' $1 #deletes whitespace lines
sort $1 -o $1 #sorts file
duplicates=`sed 's/^[^"]*"\([^"]*\)".*/\1/' $1 | uniq -d`
if [ ! -z "${duplicates}" ]; then
echo "error: Found duplicated keys"
echo "error: $duplicates in file: $1"
exit $es_duplicates
fi
}
find ./$project_name -name 'Localizable.strings' |
while read file; do
sort_and_find_duplicates $file
done
Trước tiên, chúng ta xác định tên của project, ở ví dụ này là CleanLocalizableExample
và 1 exit status
nết như tìm thấy 1 duplicated key
. Chúng ta sau đó define 1 method mà đầu vào là 1 file, remove white spaces
trong file và sắp xếp từng line một. Với 1 chút sed®ex
chúng ta có thể trích xuất các các localizable key
từ file bằng cách lấy các string ở giữa dấu ngoặc kép đầu tiên ( i.e "Fruit:Apple" = "苹果” -> "Fruit:Apple") Sau khi lấy được key chúng ta sẽ kiểm tra xem các keys có bị duplicate hay không. Nếu tìm được 1 duplicated key nào đó, script sẽ output 1 error hoặc warning. Phần cuối của script này sẽ xác định tất cacr các file có tên Localizable.strings
từ root của project và thực hiện sort_and_find_duplicates
trên đó. Tiếp theo chúng ta sẽ add 1 functions kiểm tra xem các key trong base localizable có match hết với các key trong các file localizable khác hay không. Điều này sẽ đảm bảo các developer khong quên việc thêm hoặc xoá cặp key/value in mỗi file khi mà sửa đổi chúng.
Find Key not match in multiple localizable files
es_match=8
keys_match() {
echo "== Checking if keys match in localizable files: $1 =="
base_keys=`sed 's/^[^"]*"\([^"]*\)".*/\1/' $development_file`
localizable_strings=`sed 's/^[^"]*"\([^"]*\)".*/\1/' $1`
is_different=`diff <(echo "$base_keys") <(echo "$localizable_strings")`
if [ ! -z "${is_different}" ]; then
echo "error: Localizable string keys do not match"
echo "error: $is_different in file: $1"
exit $es_match
fi
}
Trong method keys_match
, chúng ta lưu trữ tất cả keys trong file base localizable
vào 1 biến tên base_keys
và lưu tất cả keys của các file localizable khác vào biến localizable_key
. Chúng ta sử dụng sed
để trích xuất các keys như method truwocs và diff
để check có sự khác biệt giữa các key hay không. Nếu có keys nào trong các file localizable mà khác nhau, Xcode sẽ throw ra build error
và output ra key đó ở file nào. Và lúc này, các files đã được sắp xếp, ko có duplicated keys, và tất cả các file đều chứa chúng các keys giống nhau, tiếp theo, 2 methods này sẽ check về việc sử dụng thực tế của các keys.
Unused & Undefined Keys:
es_not_included=7
keys_not_used() {
echo "== Checking keys not used in code =="
sed 's/^[^"]*"\([^"]*\)".*/\1/' $development_file |
while read key; do
exist=`grep -rl "NSLocalizedString(\"$key\"" --include \*.swift --include \*.m ./$project_name/*`
if [ -z "${exist}" ]; then
echo "warning: Found keys not used in code"
echo "warning: \"$key\" is not used being used"
fi
done
}
keys_not_included() {
echo "== Checking keys not included in localizable =="
base_keys=`sed 's/^[^"]*"\([^"]*\)".*/\1/' $development_file`
# grep NSLocalizedString("anything until first quotes" | sed everything in between quotes | sort and unique
grep -r -o "NSLocalizedString(\"[^\"]*\"" --include \*.swift --include \*.m ./$project_name/* --exclude ./$project_name/NSLocalizedString.swift |
grep -v "%d" |
sed 's/^[^"]*"\([^"]*\)".*/\1/' |
sort -u |
while read key; do
if [[ $base_keys != *$key* ]]; then
echo "error: Found keys not included in localizable file"
echo "error: \"$key\" not define in file: $1"
exit $es_not_included
fi
done
}
keys_not_used
method sẽ loop qua toàn bộ keys và apply 1 grep
cho NSLocalizedString("$key" trong toàn bộ các file .m và .swift thuộc project). Nếu keys nào đó ko được sử dụng trong bất kỳ file nào, script sẽ throws ra 1 Xcode warning. Method này sẽ cần 1 chút tinh chỉnh nhỏ cho phù hợp với project của bạn. Nếu như project bao gồm cả localized string cho việc test, loại trừ đường dẫn trong grep
function. Kết quả của method sẽ output ra các keys ko được sử dụng và throws ra 1 warning chứ ko phải error vì nếu có 1key ko được sử dụng thì hoàn toàn vô hại từ góc nhìn của user.
keys_not_include
tương tự như method keys_not_used
cũng sẽ kiểm tra qua tất cả các keys xem trong các file .m và .swift có những key nào chưa có trong file base localizable. Nếu có, nó sẽ throws ra 1 error.
Toàn bộ file script sẽ như sau:
#!/bin/bash
project_name="CleanLocalizableExample"
development_file="./$project_name/en.lproj/Localizable.strings"
es_duplicates=9
es_match=8
es_not_included=7
sort_and_find_duplicates() {
echo "== Sorting localizable files: $1 =="
sed -i '' '/^$/d' $1 #deletes whitespace lines
sort $1 -o $1 #sorts file
duplicates=`sed 's/^[^"]*"\([^"]*\)".*/\1/' $1 | uniq -d`
if [ ! -z "${duplicates}" ]; then
echo "error: Found duplicated keys"
echo "error: $duplicates in file: $1"
exit $es_duplicates
fi
}
keys_match() {
echo "== Checking if keys match in localizable files: $1 =="
base_keys=`sed 's/^[^"]*"\([^"]*\)".*/\1/' $development_file`
localizable_keys=`sed 's/^[^"]*"\([^"]*\)".*/\1/' $1`
is_different=`diff <(echo "$base_keys") <(echo "$localizable_keys")`
if [ ! -z "${is_different}" ]; then
echo "error: Localizable string keys do not match"
echo "error: $is_different in file: $1"
exit $es_match
fi
}
keys_not_used() {
echo "== Checking keys not used in code =="
sed 's/^[^"]*"\([^"]*\)".*/\1/' $development_file |
while read key; do
exist=`grep -rl "NSLocalizedString(\"$key\"" --include \*.swift --include \*.m ./$project_name/*`
if [ -z "${exist}" ]; then
echo "warning: Found keys not used in code"
echo "warning: \"$key\" is not used being used"
fi
done
}
keys_not_included() {
echo "== Checking keys not included in localizable =="
base_keys=`sed 's/^[^"]*"\([^"]*\)".*/\1/' $development_file`
# grep NSLocalizedString("anything until first quotes" | sed everything in between quotes | sort and unique
grep -r -o "NSLocalizedString(\"[^\"]*\"" --include \*.swift --include \*.m ./$project_name/* --exclude ./$project_name/NSLocalizedString.swift |
grep -v "%d" |
sed 's/^[^"]*"\([^"]*\)".*/\1/' |
sort -u |
while read key; do
if [[ $base_keys != *$key* ]]; then
echo "error: Found keys not included in localizable file"
echo "error: \"$key\" not define in file: $1"
exit $es_not_included
fi
done
}
find ./$project_name -name 'Localizable.strings' |
while read file; do
sort_and_find_duplicates $file
keys_match $file
echo ""
done
keys_not_used
keys_not_included
Use
Save file trên vào trong scripts
và thêm nó vào post build script
trong Xcode
Build project và bạn sẽ thấy:
Reference
https://buildingvts.com/clean-ios-localizable-files-8b910413b985
All rights reserved