Skip to content

Instantly share code, notes, and snippets.

@talanov
Last active February 18, 2019 10:08
Show Gist options
  • Save talanov/497c6d15f8921818b6ee42720da6f4f1 to your computer and use it in GitHub Desktop.
Save talanov/497c6d15f8921818b6ee42720da6f4f1 to your computer and use it in GitHub Desktop.
Automatically extract translatable strings from Xcode storyboards and update .strings files. Original version: https://gist.github.com/ole/5007019. Updated for Xcode 10 & Swift 4.2
#!/bin/sh
#
# update_storyboard_strings.sh - automatically extract translatable strings from storyboards and update strings files updated for Xcode 10.1 & Swift 4.2
# Original script — https://gist.github.com/ole/5007019
# Folder structure to eliminate duplicates & to make xib localization work.
# Instruction:
# 1. Go to XIB / Storyboard, check out right bar (Inspector), go to Localization and check out desired language.
# 2. Create Localizable.strings file @ LANGUAGECODE.lproj with localized strings. (// TODO: Make it global)
# 3. Build.
# ****************************
# FOLDER STRUCTURE
# ****************************
# |Storyboard folder
# |---| Base.lproj
# |---|--- FILE.storyboard
# |---| LANGUAGECODE.lproj
# |---|--- Localizable.strings
# | Folder
# |---| Base.lproj
# |---|--- FILE.xib
# |---| LANGUAGECODE.lproj
# |---|--- Localizable.strings
# ****************************
# FOLDER STRUCTURE END
# ****************************
storyboardExt=".storyboard"
xibExt=".xib"
stringsExt=".strings"
newStringsExt=".strings.new"
oldStringsExt=".strings.old"
bakStringsExt=".strings--"
localeDirExt=".lproj"
oldIFS=$IFS
IFS=$'\n'
# Find storyboard file full path inside project folder
for storyboardPath in `find . -name "*$storyboardExt" -print`
do
# Get Base strings file full path
baseStringsPath=$(echo "$storyboardPath" | sed "s/$storyboardExt/$stringsExt/")
echo "Base storyboard's string file path: "$baseStringsPath
# Create base strings file if it doesn't exist
if ! [ -f $baseStringsPath ]; then
touch -r $storyboardPath $baseStringsPath
# Make base strings file older than the storyboard file
touch -A -01 $baseStringsPath
fi
# Only run, if storyboard's newer.
if find $storyboardPath -prune -newer $baseStringsPath -print | grep -q .; then
# Get storyboard file name and folder
storyboardFile=$(basename "$storyboardPath")
storyboardDir=$(dirname "$storyboardPath")
echo "\nStoryboard File: $storyboardFile\n"
# Get New Base strings file full path and strings file name
newBaseStringsPath=$(echo "$storyboardPath" | sed "s/$storyboardExt/$newStringsExt/")
stringsFile=$(basename "$baseStringsPath")
echo "Extracting strings file"
ibtool --export-strings-file $newBaseStringsPath $storyboardPath
# ibtool sometimes fails for unknown reasons with "Interface Builder could not open
# the document XXX because it does not exist."
# (maybe because Xcode is writing to the file at the same time?)
# In that case, abort the script.
if [[ $? -ne 0 ]] ; then
echo "Exiting due to ibtool error. Please run `killall -9 ibtoold` and try again."
exit 1
fi
# Only run iconv if $newBaseStringsPath exists to avoid overwriting existing
if [ -f $newBaseStringsPath ]; then
iconv -f UTF-16 -t UTF-8 $newBaseStringsPath > $baseStringsPath
rm $newBaseStringsPath
echo "Removing new file...\n"
fi
# Get all locale strings folder
for localeStringsDir in `find . -name "*$localeDirExt" -print`
do
echo "\n\nLocal string directory: $localeStringsDir\n"
# Base String File Path — базовый файл, из которого будет браться вся локаль.
baseStringFilePath="$localeStringsDir/Localizable""$stringsExt"
echo "Base strings file path: $baseStringFilePath"
# Storyboard folder
rp1=$(realpath $storyboardDir)
# Locale folder
rp2=$(realpath $localeStringsDir)
if [ -f "$baseStringFilePath" ]
then
echo "\n$baseStringFilePath found."
# Backing up Localizable.
cp $baseStringFilePath $localeStringsDir/../Localizable$stringsExt
else
echo "$baseStringFilePath not found."
# FILE NOT FOUND!
# Skip Base strings folder
if [ $rp2 == $rp1 ] || [[ $rp2 == *Base.lproj ]]
then
echo "Skip..."
else
echo $rp2
exit 1
fi
fi
if [ $rp1 != $rp2 ]
then
localeStringsPath=$localeStringsDir/$stringsFile
echo "Localized strings file: $localeStringsPath"
# If it doesn't exist -> Create. Don't forget to link it in pbxproj.
cp $baseStringsPath $localeStringsPath
echo "Created $localeStringsPath. Please link it to pbxproj."
oldLocaleStringsPath=$(echo "$localeStringsPath" | sed "s/$stringsExt/$oldStringsExt/")
echo "Temporary old file: $oldLocaleStringsPath"
cp $localeStringsPath $oldLocaleStringsPath
for string in $(cat "$baseStringFilePath")
do
# REGEX "*" = "*"
regex="\".*\".*=.*\".*\""
if [[ $string =~ $regex ]]
then
IFS='='
# ARRAY
STRINGS=()
for innerString in $string
do
innerInnerString=$(echo $innerString | awk 'match($0, /\"([^\"]|\\\")*\"/) { print substr( $0, RSTART, RLENGTH )}')
STRINGS+=($innerInnerString)
done
sed -i -- "s/${STRINGS[0]};$/${STRINGS[1]};/g" $localeStringsDir/*.strings
fi
IFS=$'\n'
done
rm $oldLocaleStringsPath
fi
# Restoring Base Localizable file
mv $localeStringsDir/../Localizable$stringsExt $baseStringFilePath
done
fi
done
# Cleaning...
find . -name "*$bakStringsExt" -exec rm -f {} \;
for stringFile in `find . -name "*$stringsExt" -print`
do
storyboard=$(echo $(dirname "$stringFile")/../Base.lproj/$(basename "$stringFile" | cut -d. -f1)$storyboardExt)
xib=$(echo $(dirname "$stringFile")/../Base.lproj/$(basename "$stringFile" | cut -d. -f1)$xibExt)
baseName=$(basename "$stringFile" | cut -d. -f1)
if [[ ( ! -f $storyboard ) && ( ! -f $xib ) && ! ( $baseName == "Localizable" || $baseName == "InfoPlist" ) ]]
then
rm $stringFile
echo "Removed duplicate: $stringFile with basename: $baseName"
fi
done
IFS=$oldIFS
@talanov
Copy link
Author

talanov commented Feb 18, 2019

Disclaimer: You shouldn't use it, unless you are trying to maintain a project which heavily relies on this script: https://gist.github.com/ole/5007019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment