Skip to content

Instantly share code, notes, and snippets.

@DmytroLisitsyn
Last active July 26, 2022 09:05
Show Gist options
  • Save DmytroLisitsyn/ae10f0a71638d17e894cef07147a861b to your computer and use it in GitHub Desktop.
Save DmytroLisitsyn/ae10f0a71638d17e894cef07147a861b to your computer and use it in GitHub Desktop.
Localization constants generator.
#!/bin/bash
#
# Copyright (C) 2019 MadAppGang Pty Ltd
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
set -e
for FILE in "$@"; do
case $FILE in
*.strings)
STRINGS_FILE=$FILE;;
*.stringsdict)
DICT_FILE=$FILE;;
*.swift)
OUTPUT_FILE=$FILE;;
*)
echo "Unexpected file type: $FILE"
exit 1;;
esac
done
PARSED=()
PARSE_STRINGS_FILE() {
if [ ! -e "$STRINGS_FILE" ]; then
return 0
fi
local RESULT=$(grep -Eoi "\".+\"[ ]*=[ ]*\".+\";" $STRINGS_FILE) # Finds localization key-values pairs.
RESULT=${RESULT//[$'\n'$'\t'\" ]/} # Removes newlines, quotations and spaces.
local ITEMS=()
IFS=';' read -ra ITEMS <<< "$RESULT" # Makes array from search result string.
unset IFS
ITEMS=("${ITEMS[@]//=*/}") # Remove value from key-value pair of match.
PARSED=("${PARSED[@]}" "${ITEMS[@]}")
return 0
}
PARSE_DICT_FILE() {
if [ ! -e "$DICT_FILE" ]; then
return 0
fi
local RESULT=$(<$DICT_FILE)
RESULT=${RESULT//[$'\n'$'\t' ]/}
RESULT=${RESULT#*<dict>}
RESULT=${RESULT/\<\/dict\>\<\/plist\>/}
RESULT=$(echo "$RESULT" | perl -pe 's/<dict><key>.*?<\/dict><\/dict>//g')
RESULT=$(echo "$RESULT" | perl -pe 's/<\/key><key>/;/g')
RESULT=$(echo "$RESULT" | perl -pe 's/<(\/)?key>//g')
local ITEMS=()
IFS=';' read -ra ITEMS <<< "$RESULT" # Makes array from search result string.
unset IFS
PARSED=("${PARSED[@]}" "${ITEMS[@]}")
return 0
}
MAKE_SPACING() {
local DEPTH=$1
local STEP=" "
local SPACING=""
for (( I=0; I<$DEPTH; I++ )); do
SPACING="$STEP$SPACING"
done
echo "$SPACING"
}
HANDLE_KEYWORD() {
case $1 in
"continue"|"switch"|"default"|"static"|"final"|"class"|"struct"|"import"|"extension"|"return"|"try"|"let"|"break"|"case"|"super"|"private"|"public"|"internal"|"guard"|"self"|"while"|"do"|"catch"|"as"|"true"|"false"|"override"|"lazy"|"get"|"set")
echo "\`$1\`";;
*)
echo $1;;
esac
}
LOWERCASE_FIRST_LETTER() {
echo "$(tr '[:upper:]' '[:lower:]' <<< ${1:0:1})${1:1}"
}
MAKE_PARAMETER_SWIFT_TYPE() {
case $1 in
*%@)
echo "String";;
*%d)
echo "Int";;
*)
echo "Unexpected parameter type: $1"
exit 1;;
esac
}
MAKE_ENTRY() {
local ENTRY=$1
local KEY=$2
local DEPTH=$3
local SPACING=$(MAKE_SPACING $DEPTH)
local NAME=$(LOWERCASE_FIRST_LETTER $ENTRY)
NAME=${NAME//%[@d]/}
NAME=$(HANDLE_KEYWORD $NAME)
local PARAMETERS=$(echo $ENTRY | grep -Eo "[A-Z]*[a-z0-9]*%[@d]")
PARAMETERS=${PARAMETERS//[$'\n']/;}
IFS=';' read -ra PARAMETERS <<< "$PARAMETERS"
unset IFS
local PARAMETERS_LENGTH=${#PARAMETERS[@]}
if [ $PARAMETERS_LENGTH -eq 0 ]; then
echo "${SPACING}static let $NAME = \"$KEY\".localized()"
else
local INPUT_ARGS=""
local OUTPUT_ARGS=""
for (( I=0; I<$PARAMETERS_LENGTH; I++ )); do
local PARAMETER=$(LOWERCASE_FIRST_LETTER ${PARAMETERS[$I]})
local TYPE=$(MAKE_PARAMETER_SWIFT_TYPE $PARAMETER)
PARAMETER=${PARAMETER//%[@d]/}
INPUT_ARGS="${INPUT_ARGS}$PARAMETER: $TYPE"
OUTPUT_ARGS="${OUTPUT_ARGS}$(HANDLE_KEYWORD $PARAMETER)"
if [ $I -lt $(expr $PARAMETERS_LENGTH - 1) ]; then
INPUT_ARGS="$INPUT_ARGS, "
OUTPUT_ARGS="${OUTPUT_ARGS}, "
fi
done
local KEY_SPACING=$(MAKE_SPACING $DEPTH+1)
echo "${SPACING}static func $NAME($INPUT_ARGS) -> String {\n${KEY_SPACING}return \"$KEY\".localized(arguments: $OUTPUT_ARGS)\n${SPACING}}"
fi
}
MAKE_ENTRIES() {
local DEPTH=$1
local HIERARCHY=()
local HIERARCHY_DEPTH=0
for KEY in "${PARSED[@]}"; do
local COMPONENTS=()
IFS='.' read -ra COMPONENTS <<< "$KEY"
unset IFS
local LENGTH=${#COMPONENTS[@]}
local NEW_HIERARCHY_DEPTH=$(expr $LENGTH - 1)
local NEW_HIERARCHY=(${COMPONENTS[@]:0:$NEW_HIERARCHY_DEPTH})
local ENTRY=${COMPONENTS[$NEW_HIERARCHY_DEPTH]}
local MAX_COMMON_HIERARCHY_DEPTH=$(( $NEW_HIERARCHY_DEPTH<$HIERARCHY_DEPTH ? $NEW_HIERARCHY_DEPTH : $HIERARCHY_DEPTH ))
local COMMON_HIERARCHY_DEPTH=0
for (( I=0; I<$MAX_COMMON_HIERARCHY_DEPTH; I++ )); do
if [[ "${HIERARCHY[$I]}" == "${NEW_HIERARCHY[$I]}" ]]; then
COMMON_HIERARCHY_DEPTH=$(expr $COMMON_HIERARCHY_DEPTH + 1)
else
break
fi
done
local BRACKETS_TO_CLOSE=$(expr $HIERARCHY_DEPTH - $COMMON_HIERARCHY_DEPTH)
for (( I=$HIERARCHY_DEPTH-1; I>=$COMMON_HIERARCHY_DEPTH; I-- )); do
local SPACING=$(MAKE_SPACING $(expr $I + $DEPTH))
echo "${SPACING}}"
done
for (( I=$COMMON_HIERARCHY_DEPTH; I<$NEW_HIERARCHY_DEPTH; ++I )); do
local SPACING=$(MAKE_SPACING $(expr $I + $DEPTH))
echo "${SPACING}enum ${COMPONENTS[$I]} {"
done
local ENTRY_DEPTH=$(expr $NEW_HIERARCHY_DEPTH + $DEPTH)
echo "$(MAKE_ENTRY $ENTRY $KEY $ENTRY_DEPTH)"
HIERARCHY=("${NEW_HIERARCHY[@]}")
HIERARCHY_DEPTH=$NEW_HIERARCHY_DEPTH
done
for (( I=$HIERARCHY_DEPTH-1; I>=0; I-- )); do
local SPACING=$(MAKE_SPACING $(expr $I + $DEPTH))
echo "${SPACING}}"
done
}
PARSE_STRINGS_FILE
PARSE_DICT_FILE
IFS=$'\n' PARSED=($(sort <<< "${PARSED[*]}")) # Sorts array of source keys.
unset IFS
echo "//
// This file is generated by Kassandra - localization constants generator.
//
// If you have any issues or just want to say thanks, contact Dmytro (dl@madappgang.com).
//
import Foundation
enum Localizable {
$(MAKE_ENTRIES 1)
}
extension String {
public func localized(arguments: CVarArg..., comment: String = \"\") -> String {
if arguments.isEmpty {
return NSLocalizedString(self, comment: comment)
} else {
return withVaList(arguments) { arguments -> String in
let format = NSLocalizedString(self, comment: comment)
return NSString(format: format, arguments: arguments) as String
}
}
}
}" > $OUTPUT_FILE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment